Added Transactions export / import

od
Laurent 6 years ago
parent 18eb54309e
commit 83dbad0e0f
  1. 3
      app/src/main/java/net/pokeranalytics/android/model/realm/Transaction.kt
  2. 12
      app/src/main/java/net/pokeranalytics/android/model/realm/TransactionType.kt
  3. 23
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt
  4. 8
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SettingRow.kt
  5. 6
      app/src/main/java/net/pokeranalytics/android/util/csv/CSVDescriptor.kt
  6. 16
      app/src/main/java/net/pokeranalytics/android/util/csv/PACSVDescriptor.kt
  7. 18
      app/src/main/java/net/pokeranalytics/android/util/csv/ProductCSVDescriptors.kt
  8. 7
      app/src/main/java/net/pokeranalytics/android/util/csv/SessionCSVDescriptor.kt
  9. 41
      app/src/main/java/net/pokeranalytics/android/util/csv/SessionField.kt
  10. 2
      app/src/main/java/net/pokeranalytics/android/util/csv/SessionTransactionCSVDescriptor.kt
  11. 78
      app/src/main/java/net/pokeranalytics/android/util/csv/TransactionCSVDescriptor.kt
  12. 2
      app/src/main/res/values/strings.xml

@ -30,13 +30,14 @@ open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSo
companion object {
fun newInstance(realm: Realm, bankroll: Bankroll, date: Date? = null, type: TransactionType, amount: Double): Transaction {
fun newInstance(realm: Realm, bankroll: Bankroll, date: Date? = null, type: TransactionType, amount: Double, comment: String? = null): Transaction {
val transaction = realm.copyToRealm(Transaction())
transaction.date = date ?: Date()
transaction.amount = amount
transaction.type = type
transaction.bankroll = bankroll
transaction.comment = comment ?: ""
return transaction
}

@ -69,6 +69,18 @@ open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentab
throw PAIllegalStateException("Transaction type ${value.name} should exist in database!")
}
fun getOrCreate(realm: Realm, name: String, additive: Boolean): TransactionType {
val type = realm.where(TransactionType::class.java).equalTo("name", name).findFirst()
return if (type != null) {
type
} else {
val transactionType = TransactionType()
transactionType.name = name
transactionType.additive = additive
realm.copyToRealm(transactionType)
}
}
}
@Ignore

@ -18,6 +18,7 @@ import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.realm.Currency
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.ui.activity.*
import net.pokeranalytics.android.ui.activity.components.RequestCode
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
@ -136,9 +137,8 @@ class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRep
SettingRow.CONTACT_US -> parentActivity?.openContactMail(R.string.contact)
SettingRow.BUG_REPORT -> parentActivity?.openContactMail(R.string.bug_report_subject, Realm.getDefaultInstance().path)
SettingRow.CURRENCY -> CurrenciesActivity.newInstanceForResult(this@SettingsFragment, RequestCode.CURRENCY.value)
SettingRow.EXPORT_CSV -> {
this.csvExport()
}
SettingRow.EXPORT_CSV_SESSIONS -> this.sessionsCSVExport()
SettingRow.EXPORT_CSV_TRANSACTIONS -> this.transactionsCSVExport()
SettingRow.FOLLOW_US -> {
when (position) {
0 -> parentActivity?.openUrl(URL.BLOG.value)
@ -211,17 +211,24 @@ class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRep
}
}
private fun csvExport() {
private fun transactionsCSVExport() {
val transactions = getRealm().where(Transaction::class.java).findAll().sort("date")
val csv = ProductCSVDescriptors.pokerAnalyticsAndroidTransactions.toCSV(transactions)
this.shareCSV(csv, "Transactions")
}
private fun sessionsCSVExport() {
val sessions = getRealm().where(Session::class.java).findAll().sort("startDate")
val csv = ProductCSVDescriptors.pokerAnalyticsAndroid.toCSV(sessions)
this.shareCSV(csv, "Sessions")
}
Timber.d("CSV = $csv")
private fun shareCSV(content: String, dataType: String) {
try {
val fileName = "sessions_${Date().dateTimeFileFormatted}.csv"
FileUtils.writeFileToFilesDir(csv, fileName, requireContext())
this.shareFile(fileName, "Poker Analytics Export", "CSV Sessions")
val fileName = "${dataType.toLowerCase()}_${Date().dateTimeFileFormatted}.csv"
FileUtils.writeFileToFilesDir(content, fileName, requireContext())
this.shareFile(fileName, "Poker Analytics Export", "CSV $dataType")
} catch (e: IOException) {
Toast.makeText(requireContext(), "File write failed: ${e.message}", Toast.LENGTH_LONG).show()
}

@ -30,7 +30,8 @@ enum class SettingRow : RowRepresentable {
CURRENCY,
// Export
EXPORT_CSV,
EXPORT_CSV_SESSIONS,
EXPORT_CSV_TRANSACTIONS,
// Data management
CUSTOM_FIELD,
@ -68,7 +69,7 @@ enum class SettingRow : RowRepresentable {
rows.addAll(arrayListOf(CURRENCY))
rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.export))
rows.addAll(arrayListOf(EXPORT_CSV))
rows.addAll(arrayListOf(EXPORT_CSV_SESSIONS, EXPORT_CSV_TRANSACTIONS))
rows.add(
CustomizableRowRepresentable(
@ -105,7 +106,8 @@ enum class SettingRow : RowRepresentable {
FOLLOW_US -> R.string.follow_us
LANGUAGE -> R.string.language
CURRENCY -> R.string.currency
EXPORT_CSV -> R.string.csv
EXPORT_CSV_SESSIONS -> R.string.sessions_csv
EXPORT_CSV_TRANSACTIONS -> R.string.transactions_csv
GDPR -> R.string.gdpr
POKER_RUMBLE -> R.string.poker_rumble
DISCORD -> R.string.join_discord

@ -83,15 +83,15 @@ abstract class DataCSVDescriptor<T : Identifiable>(source: DataSource, vararg el
dataSequence.forEach { data ->
val line = mutableListOf<String>()
this.fields.forEach { field ->
line.add(this.toCSV(data, field))
line.add(this.toCSV(data, field) ?: "")
}
lines.add(line.joinToString(","))
}
return lines.joinToString("\n")
}
protected open fun toCSV(data: T, field: CSVField): String {
return ""
protected open fun toCSV(data: T, field: CSVField): String? {
return null
}
// abstract fun

@ -55,7 +55,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource, private var
sameDaySessionCount = 0
}
currentDay = value
}
} else {}
}
is SessionField.End -> {
endDate = field.parse(value)
@ -104,7 +104,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource, private var
session.result?.buyin = buyin
if (session.type == Session.Type.TOURNAMENT.ordinal) {
session.tournamentEntryFee = buyin
}
} else {}
}
is SessionField.CashedOut -> session.result?.cashout = field.parse(value)
is SessionField.NetResult -> session.result?.netResult = field.parse(value)
@ -135,18 +135,18 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource, private var
}
session.game = realm.getOrCreate(limitAndGame.trim())
}
} else {}
}
is SessionField.Game -> {
if (value.isNotEmpty()) {
session.game = realm.getOrCreate(value)
}
} else {}
}
is SessionField.Location -> {
val trimmedValue = value.trim()
if (trimmedValue.isNotEmpty()) {
session.location = realm.getOrCreate(trimmedValue)
}
} else {}
}
is SessionField.Bankroll -> bankrollName = value
is SessionField.LimitType -> session.limit = Limit.getInstance(value)?.ordinal
@ -160,13 +160,13 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource, private var
val sb = field.parse(value)
if (sb != null && sb > 0.0) {
session.cgSmallBlind = sb
}
} else {}
}
is SessionField.BigBlind -> {
val bb = field.parse(value)
if (bb != null && bb > 0.0) {
session.cgBigBlind = bb
}
} else {}
}
is SessionField.TableSize -> session.tableSize = TableSize.valueForLabel(value)
is SessionField.TournamentPosition -> session.result?.tournamentFinalPosition =
@ -174,7 +174,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource, private var
is SessionField.TournamentName -> {
if (value.isNotEmpty()) {
session.tournamentName = realm.getOrCreate(value)
}
} else {}
}
is SessionField.TournamentTypeName -> session.tournamentType =
TournamentType.getValueForLabel(value)?.ordinal

@ -17,7 +17,8 @@ class ProductCSVDescriptors {
runGoodCashGames,
runGoodTournaments,
pokerAnalyticsiOS,
pokerAnalyticsAndroid
pokerAnalyticsAndroid,
pokerAnalyticsAndroidTransactions
)
private val pokerAgent: CSVDescriptor
@ -189,6 +190,21 @@ class ProductCSVDescriptors {
)
}
val pokerAnalyticsAndroidTransactions: TransactionCSVDescriptor
get() {
return TransactionCSVDescriptor(
DataSource.POKER_ANALYTICS,
TrField.TransactionDate("Date", dateFormat = "MM/dd/yy HH:mm:ss"),
TrField.Amount("Amount"),
TrField.TransactionType("Type"),
TrField.BankrollName("Bankroll"),
TrField.Live("Live"),
TrField.CurrencyCode("Currency Code"),
TrField.CurrencyRate("Currency Rate"),
TrField.Comment("Comment")
)
}
val pokerAnalyticsAndroid: SessionCSVDescriptor
get() {
return SessionCSVDescriptor(

@ -15,8 +15,9 @@ class SessionCSVDescriptor(source: DataSource, isTournament: Boolean?, vararg el
return this.parseSession(realm, record)
}
override fun toCSV(data: Session, field: CSVField): String {
val string = when (field) {
override fun toCSV(data: Session, field: CSVField): String? {
return when (field) {
is SessionField.Start -> field.format(data.startDate)
is SessionField.End -> field.format(data.endDate)
is SessionField.Break -> field.format(data.breakDuration.toDouble())
@ -49,7 +50,7 @@ class SessionCSVDescriptor(source: DataSource, isTournament: Boolean?, vararg el
is SessionField.Comment -> data.comment
else -> null
}
return string ?: ""
}
}

@ -4,14 +4,39 @@ import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.TournamentFeature
import java.util.*
//sealed class TransactionField {
//
// data class TransactionType(
// override var header: String,
// override var callback: ((String) -> net.pokeranalytics.android.model.realm.TransactionType?)? = null
// ) : DataCSVField<net.pokeranalytics.android.model.realm.TransactionType>
//
//}
sealed class TrField {
data class TransactionDate(
override var header: String,
override var callback: ((String) -> Date?)? = null,
override val dateFormat: String? = null
) : DateCSVField
data class Amount(
override var header: String,
override var callback: ((String) -> Double?)? = null,
override val numberFormat: String? = null
) : NumberCSVField
data class BankrollName(override var header: String) : CSVField
data class Live(
override var header: String,
override var callback: ((String) -> Boolean?)? = null
) : BooleanCSVField
data class CurrencyCode(override var header: String) : CSVField
data class CurrencyRate(
override var header: String,
override var callback: ((String) -> Double?)? = null,
override val numberFormat: String? = null
) : NumberCSVField
data class TransactionType(override var header: String) : CSVField
data class Comment(override var header: String) : CSVField
}
/**
* The enumeration of Session fields

@ -65,7 +65,7 @@ class SessionTransactionCSVDescriptor(source: DataSource, private var isTourname
var buyin: Double? = null
var cashedOut: Double? = null
fields.forEach { field ->
this.fields.forEach { field ->
val index = this.fieldMapping[field]
if (index != null) {

@ -0,0 +1,78 @@
package net.pokeranalytics.android.util.csv
import io.realm.Realm
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.model.realm.TransactionType
import net.pokeranalytics.android.model.utils.DataUtils
import org.apache.commons.csv.CSVRecord
import timber.log.Timber
import java.util.*
class TransactionCSVDescriptor(source: DataSource, vararg elements: CSVField) :
DataCSVDescriptor<Transaction>(source, *elements) {
override fun parseData(realm: Realm, record: CSVRecord): Transaction? {
var date: Date? = null
var typeName: String? = null
var bankrollName: String? = null
var amount: Double? = null
var comment: String? = null
var live = false
var currencyCode: String? = null
var currencyRate: Double? = null
for (field in this.fields) {
val index = this.fieldMapping[field]
if (index != null) {
val value = record.get(index)
when (field) {
is TrField.TransactionDate -> date = field.parse(value)
is TrField.TransactionType -> typeName = value
is TrField.Amount -> amount = field.parse(value)
is TrField.BankrollName -> bankrollName = value
is TrField.Live -> live = field.parse(value) ?: true
is TrField.CurrencyCode -> currencyCode = value
is TrField.CurrencyRate -> currencyRate = field.parse(value)
is TrField.Comment -> comment = value
}
}
}
if (date != null && amount != null && typeName != null && bankrollName != null) {
val type = TransactionType.getOrCreate(realm, typeName, amount > 0)
if (DataUtils.transactionUnicityCheck(realm, date, amount, type)) {
val bankroll = Bankroll.getOrCreate(realm, bankrollName, live, currencyCode, currencyRate)
return Transaction.newInstance(realm, bankroll, date, type, amount, comment)
} else {
Timber.d("Transaction already exists")
}
} else {
Timber.d("Can't import transaction: date=$date, amount=$amount, type=$typeName")
}
return null
}
override fun toCSV(data: Transaction, field: CSVField): String? {
return when(field) {
is TrField.TransactionDate -> field.format(data.date)
is TrField.TransactionType -> data.type?.name
is TrField.Amount -> field.format(data.amount)
is TrField.BankrollName -> data.bankroll?.name
is TrField.Live -> field.format(data.bankroll?.live)
is TrField.CurrencyCode -> data.bankroll?.currency?.code
is TrField.CurrencyRate -> field.format(data.bankroll?.currency?.rate)
is TrField.Comment -> data.comment
else -> throw PAIllegalStateException("unmanaged field: $field")
}
}
}

@ -768,5 +768,7 @@
<string name="join_discord">Join us on Discord!</string>
<string name="discord_feed_message">We\'ve opened our Discord channel! Come to hang out, talk about poker or about the app!</string>
<string name="good_for_you">Good for you!</string>
<string name="sessions_csv">Sessions (CSV)</string>
<string name="transactions_csv">Transactions (CSV)</string>
</resources>

Loading…
Cancel
Save