Added iOS CSV import + custom field creation

csv
Laurent 6 years ago
parent e36b08aee3
commit 9dca2eb647
  1. 2
      app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt
  2. 25
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomField.kt
  3. 5
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  4. 18
      app/src/main/java/net/pokeranalytics/android/util/csv/CSVDescriptor.kt
  5. 16
      app/src/main/java/net/pokeranalytics/android/util/csv/CSVField.kt
  6. 44
      app/src/main/java/net/pokeranalytics/android/util/csv/ProductCSVDescriptors.kt
  7. 8
      app/src/main/java/net/pokeranalytics/android/util/csv/SessionCSVDescriptor.kt
  8. 19
      app/src/main/java/net/pokeranalytics/android/util/csv/SessionField.kt
  9. 8
      app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt

@ -167,6 +167,8 @@ class PokerAnalyticsMigration : RealmMigration {
} }
} }
schema.get("Session")?.addField("tournamentPrizepool", Double::class.java)?.setNullable("tournamentPrizepool", true)
currentVersion++ currentVersion++
} }
} }

@ -23,12 +23,29 @@ import net.pokeranalytics.android.ui.view.rowrepresentable.CustomFieldRow
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import net.pokeranalytics.android.util.enumerations.IntIdentifiable import net.pokeranalytics.android.util.enumerations.IntIdentifiable
import net.pokeranalytics.android.util.enumerations.IntSearchable
import net.pokeranalytics.android.util.extensions.findByName
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable { open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable {
companion object {
fun getOrCreate(realm: Realm, name: String, type: Type) : CustomField {
val cf = realm.findByName(CustomField::class.java, name)
cf?.let {
return it
}
val customField = CustomField()
customField.name = name
customField.type = type.uniqueIdentifier
return realm.copyToRealm(customField)
}
}
@Ignore @Ignore
override val realmObjectClass: Class<out Identifiable> = CustomField::class.java override val realmObjectClass: Class<out Identifiable> = CustomField::class.java
@ -39,7 +56,13 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
IntIdentifiable { IntIdentifiable {
LIST(0, R.string.enum_custom_field_type), LIST(0, R.string.enum_custom_field_type),
NUMBER(1, R.string.number), NUMBER(1, R.string.number),
AMOUNT(2, R.string.amount) AMOUNT(2, R.string.amount);
companion object : IntSearchable<Type> {
override fun valuesInternal(): Array<Type> {
return values()
}
}
} }
/** /**

@ -314,6 +314,11 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
// The features of the tournament, like Knockout, Shootout, Turbo... // The features of the tournament, like Knockout, Shootout, Turbo...
var tournamentFeatures: RealmList<TournamentFeature> = RealmList() var tournamentFeatures: RealmList<TournamentFeature> = RealmList()
/**
* the prizepool of the tournament
*/
var tournamentPrizepool: Double? = null
// The custom fields values // The custom fields values
var customFieldEntries: RealmList<CustomFieldEntry> = RealmList() var customFieldEntries: RealmList<CustomFieldEntry> = RealmList()

@ -136,13 +136,27 @@ abstract class CSVDescriptor(var source: DataSource, vararg elements: CSVField)
val customFields = realm.where(CustomField::class.java).findAll() val customFields = realm.where(CustomField::class.java).findAll()
val headers = record.toSet() val headers = record.toSet()
headers.forEach { header -> headers.forEach { header ->
// automatically creates custom field if necessary
if (source == DataSource.POKER_ANALYTICS) {
val splitter = "|"
if (header.contains(splitter)) {
val info = header.split(splitter)
val typeIdentifier = header.last().toInt()
val type = CustomField.Type.valueByIdentifier(typeIdentifier)
CustomField.getOrCreate(realm, info.first(), type)
}
}
// maps header with custom fields
val customField = customFields.firstOrNull { it.name == header } val customField = customFields.firstOrNull { it.name == header }
customField?.let { customField?.let {
if (it.isListType) { if (it.isListType) {
val f = MappedCustomCSVField.List(header, null, it)
fields.add(f)
} else { } else {
val f = MappedCustomCVSField.Number(header, null, "", it) val f = MappedCustomCSVField.Number(header, null, "", it)
fields.add(f) fields.add(f)
} }

@ -16,7 +16,9 @@ interface NumberCSVField: TypedCSVField<Double> {
val numberFormat: String? val numberFormat: String?
override fun parse(value: String) : Double? { companion object {
fun defaultParse(value: String) : Double? {
if (value.isEmpty()) { if (value.isEmpty()) {
return null return null
@ -27,12 +29,22 @@ interface NumberCSVField: TypedCSVField<Double> {
return try { return try {
formatter.parse(value).toDouble() formatter.parse(value).toDouble()
} catch (e: ParseException) { } catch (e: ParseException) {
Timber.d("Field ${header} > Unparseable number: $value") Timber.d("Field > Unparseable number: $value")
null null
} }
} }
} }
override fun parse(value: String) : Double? {
this.callback?.let { cb ->
return cb(value)
}
return defaultParse(value)
}
}
interface DataCSVField<T> : TypedCSVField<T> { interface DataCSVField<T> : TypedCSVField<T> {
override fun parse(value: String): T? { override fun parse(value: String): T? {

@ -12,7 +12,9 @@ class ProductCSVDescriptors {
ProductCSVDescriptors.pokerIncomeCash, ProductCSVDescriptors.pokerIncomeCash,
ProductCSVDescriptors.pokerBankrollTracker, ProductCSVDescriptors.pokerBankrollTracker,
ProductCSVDescriptors.runGoodCashGames, ProductCSVDescriptors.runGoodCashGames,
ProductCSVDescriptors.runGoodTournaments ProductCSVDescriptors.runGoodTournaments,
ProductCSVDescriptors.iOSPokerAnalytics
) )
val pokerIncomeCash: CSVDescriptor = SessionCSVDescriptor( val pokerIncomeCash: CSVDescriptor = SessionCSVDescriptor(
@ -65,27 +67,33 @@ class ProductCSVDescriptors {
val iOSPokerAnalytics: CSVDescriptor = SessionCSVDescriptor( val iOSPokerAnalytics: CSVDescriptor = SessionCSVDescriptor(
DataSource.POKER_ANALYTICS, DataSource.POKER_ANALYTICS,
true, true,
SessionField.Start("Start date", dateFormat = "MM/dd/yy HH:mm"), SessionField.Start("Start date", dateFormat = "MM/dd/yy HH:mm:ss"),
SessionField.End("End date", dateFormat = "MM/dd/yy HH:mm"), SessionField.End("End date", dateFormat = "MM/dd/yy HH:mm:ss"),
SessionField.Break("Break", callback = { string ->
val number = NumberCSVField.defaultParse(string)
return@Break number?.times(1000.0)
}),
SessionField.SessionType("Type"), SessionField.SessionType("Type"),
SessionField.Stakes("Stakes"), SessionField.Live("Live"),
SessionField.Buyin("Buy-in"),
SessionField.CashedOut("Cashed Out"),
SessionField.NetResult("Net Result"),
SessionField.Tips("Tips"),
SessionField.LimitType("Limit"),
SessionField.Game("Game"), SessionField.Game("Game"),
SessionField.Live("Live/Room"),
SessionField.Location("Location"),
SessionField.NumberOfTables("Number of tables"),
SessionField.TableSize("Table size"), SessionField.TableSize("Table size"),
SessionField.Location("Location"),
SessionField.NumberOfTables("Tables"),
SessionField.Bankroll("Bankroll"), SessionField.Bankroll("Bankroll"),
SessionField.CurrencyCode("Currency"), SessionField.CurrencyCode("Currency Code"),
SessionField.CurrencyRate("Rate"), SessionField.SmallBlind("Small Blind"),
SessionField.Comment("Comment"), SessionField.BigBlind("Big Blind"),
SessionField.Buyin("Buy-in"), SessionField.TournamentType("Tournament Type"),
SessionField.CashedOut("Net result"), SessionField.TournamentEntryFee("Entry fee"),
SessionField.TournamentNumberOfPlayers("Number of players"),
SessionField.Break("breakminutes"), SessionField.TournamentPrizePool("Prize Pool"),
SessionField.LimitType("limit"), SessionField.TournamentPosition("Position"),
SessionField.Tips("expensesfromstack"), SessionField.Comment("Comment")
SessionField.TournamentNumberOfPlayers("player"),
SessionField.TournamentPosition("place")
) )
val runGoodTournaments: CSVDescriptor = SessionCSVDescriptor( val runGoodTournaments: CSVDescriptor = SessionCSVDescriptor(

@ -155,6 +155,9 @@ class SessionCSVDescriptor(source: DataSource, private var isTournament: Boolean
is SessionField.EndTime -> { is SessionField.EndTime -> {
endDate?.setHourMinutes(value) endDate?.setHourMinutes(value)
} }
is SessionField.Live -> {
isLive = field.parse(value) ?: true
}
is SessionField.Buyin -> { is SessionField.Buyin -> {
val buyin = field.parse(value) val buyin = field.parse(value)
session.result?.buyin = buyin session.result?.buyin = buyin
@ -212,6 +215,7 @@ class SessionCSVDescriptor(source: DataSource, private var isTournament: Boolean
TournamentType.getValueForLabel(value)?.ordinal TournamentType.getValueForLabel(value)?.ordinal
is SessionField.TournamentNumberOfPlayers -> session.tournamentNumberOfPlayers = is SessionField.TournamentNumberOfPlayers -> session.tournamentNumberOfPlayers =
field.parse(value)?.toInt() field.parse(value)?.toInt()
is SessionField.TournamentPrizePool -> session.tournamentPrizepool = field.parse(value)
is SessionField.CurrencyCode -> currencyCode = value is SessionField.CurrencyCode -> currencyCode = value
is SessionField.CurrencyRate -> currencyRate = field.parse(value) is SessionField.CurrencyRate -> currencyRate = field.parse(value)
is SessionField.StackingIn -> { is SessionField.StackingIn -> {
@ -220,12 +224,12 @@ class SessionCSVDescriptor(source: DataSource, private var isTournament: Boolean
is SessionField.StackingOut -> { is SessionField.StackingOut -> {
stackingOut = field.parse(value) stackingOut = field.parse(value)
} }
is MappedCustomCVSField.Number -> { is MappedCustomCSVField.Number -> {
field.parse(value)?.let { field.parse(value)?.let {
session.customFieldEntries.add(it) session.customFieldEntries.add(it)
} }
} }
is MappedCustomCVSField.List -> { is MappedCustomCSVField.List -> {
field.parse(value)?.let { field.parse(value)?.let {
session.customFieldEntries.add(it) session.customFieldEntries.add(it)
} }

@ -4,7 +4,7 @@ import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.model.realm.CustomFieldEntry import net.pokeranalytics.android.model.realm.CustomFieldEntry
import java.util.* import java.util.*
sealed class MappedCustomCVSField { sealed class MappedCustomCSVField {
data class Number( data class Number(
override var header: String, override var header: String,
@ -149,7 +149,10 @@ sealed class SessionField {
override var callback: ((String) -> Boolean?)? = null) : TypedCSVField<Boolean> { override var callback: ((String) -> Boolean?)? = null) : TypedCSVField<Boolean> {
override fun parse(value: String): Boolean? { override fun parse(value: String): Boolean? {
return true return when (value) {
"Live", "1" -> true
else -> false
}
} }
} }
@ -165,10 +168,22 @@ sealed class SessionField {
override val numberFormat: String? = null override val numberFormat: String? = null
) : NumberCSVField ) : NumberCSVField
data class TournamentEntryFee(
override var header: String,
override var callback: ((String) -> Double?)? = null,
override val numberFormat: String? = null
) : NumberCSVField
data class TournamentNumberOfPlayers( data class TournamentNumberOfPlayers(
override var header: String, override var header: String,
override var callback: ((String) -> Double?)? = null, override var callback: ((String) -> Double?)? = null,
override val numberFormat: String? = null override val numberFormat: String? = null
) : NumberCSVField ) : NumberCSVField
data class TournamentPrizePool(
override var header: String,
override var callback: ((String) -> Double?)? = null,
override val numberFormat: String? = null
) : NumberCSVField
} }

@ -24,6 +24,14 @@ inline fun <reified T: Identifiable> Realm.findById(id: String) : T? {
return this.findById(T::class.java, id) return this.findById(T::class.java, id)
} }
fun <T : NameManageable> Realm.findByName(clazz: Class<T>, name: String) : T? {
return this.where(clazz).equalTo("name", name).findFirst()
}
inline fun <reified T: NameManageable> Realm.findByName(name: String) : T? {
return this.findByName(T::class.java, name)
}
fun <T: NameManageable> Realm.getOrCreate(clazz: Class<T>, name: String) : T { fun <T: NameManageable> Realm.getOrCreate(clazz: Class<T>, name: String) : T {
val instance = this.where(clazz).equalTo("name", name).findFirst() val instance = this.where(clazz).equalTo("name", name).findFirst()
return if (instance != null) { return if (instance != null) {

Loading…
Cancel
Save