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

feature/top10
Aurelien Hubert 7 years ago
commit c8b622fca2
  1. 15
      app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/ExceptionFilterInstrumentedTest.kt
  2. 3
      app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/RealmFilterInstrumentedUnitTest.kt
  3. 43
      app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt
  4. 27
      app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt
  5. 53
      app/src/main/java/net/pokeranalytics/android/model/filter/QueryType.kt
  6. 25
      app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt
  7. 33
      app/src/main/java/net/pokeranalytics/android/model/realm/FilterElement.kt
  8. 4
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterElementRow.kt

@ -2,7 +2,7 @@ package net.pokeranalytics.android.unitTests.filter
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import net.pokeranalytics.android.components.BaseFilterInstrumentedUnitTest import net.pokeranalytics.android.components.BaseFilterInstrumentedUnitTest
import net.pokeranalytics.android.exceptions.FilterValueMapException import net.pokeranalytics.android.exceptions.PokerAnalyticsException
import net.pokeranalytics.android.model.filter.QueryType import net.pokeranalytics.android.model.filter.QueryType
import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.model.realm.FilterElement import net.pokeranalytics.android.model.realm.FilterElement
@ -13,7 +13,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class ExceptionFilterInstrumentedTest: BaseFilterInstrumentedUnitTest() { class ExceptionFilterInstrumentedTest: BaseFilterInstrumentedUnitTest() {
@Test(expected = FilterValueMapException::class) @Test(expected = PokerAnalyticsException.FilterElementExpectedValueMissing::class)
fun testValueKeyFilterException() { fun testValueKeyFilterException() {
val filter = QueryType.STARTED_FROM_DATE val filter = QueryType.STARTED_FROM_DATE
val filterElement = FilterElement() val filterElement = FilterElement()
@ -27,15 +27,8 @@ class ExceptionFilterInstrumentedTest: BaseFilterInstrumentedUnitTest() {
) )
} }
@Test(expected = FilterValueMapException::class) @Test(expected = PokerAnalyticsException.FilterElementUnknownName::class)
fun testFilterException() { fun testFilterException() {
val realm = this.mockRealm FilterElement().queryType
val filter = QueryType.BLINDS
filter.updateValueMap(FilterElement())
Filter.queryOn(
realm,
Session,
arrayListOf(filter)
)
} }
} }

@ -2,6 +2,7 @@ package net.pokeranalytics.android.unitTests.filter
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import net.pokeranalytics.android.components.BaseFilterInstrumentedUnitTest import net.pokeranalytics.android.components.BaseFilterInstrumentedUnitTest
import net.pokeranalytics.android.exceptions.PokerAnalyticsException
import net.pokeranalytics.android.model.filter.QueryType import net.pokeranalytics.android.model.filter.QueryType
import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
@ -37,7 +38,7 @@ class RealmFilterInstrumentedUnitTest : BaseFilterInstrumentedUnitTest() {
val filterComponent = filter.filterElements.first() val filterComponent = filter.filterElements.first()
filterComponent?.let { filterComponent?.let {
Assert.assertEquals(QueryType.CASH, QueryType.valueOf(it.filterName)) Assert.assertEquals(QueryType.CASH, QueryType.valueOf(it.filterName ?: throw PokerAnalyticsException.FilterElementUnknownName))
} ?: run { } ?: run {
Assert.fail() Assert.fail()
} }

@ -1,25 +1,22 @@
package net.pokeranalytics.android.exceptions package net.pokeranalytics.android.exceptions
class ModelException(message: String) : Exception(message) { import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow
} class ModelException(message: String) : Exception(message)
class FormattingException(message: String) : Exception(message)
class FormattingException(message: String) : Exception(message) { class RowRepresentableEditDescriptorException(message: String) : Exception(message)
} class ConfigurationException(message: String) : Exception(message)
class RowRepresentableEditDescriptorException(message: String) : Exception(message) { sealed class PokerAnalyticsException(message: String) : Exception(message) {
object FilterElementUnknownName : PokerAnalyticsException(message = "No filterElement name was found to identify the queryType")
} object FilterElementUnknownSectionName: PokerAnalyticsException(message = "No filterElement section name was found to identify the queryType")
object FilterMissingEntity: PokerAnalyticsException(message = "This filter has no entity initialized")
class FilterValueMapException(message: String) : Exception(message) { object FilterUnhandledEntity : PokerAnalyticsException(message = "This entity is not filterable")
init { object QueryValueMapUnknown: PokerAnalyticsException(message = "fieldName is missing")
println("FilterValueMapException(): $message") object QueryTypeUnhandled: PokerAnalyticsException(message = "filter type not handled")
} object QueryValueMapUnexpectedValue: PokerAnalyticsException(message = "valueMap null not expected")
} object FilterElementExpectedValueMissing : PokerAnalyticsException(message = "filter is empty or null")
data class QueryValueMapMissingKeys(val missingKeys: List<String>) : PokerAnalyticsException(message = "valueMap does not contain $missingKeys")
class FilterMissingEntityException(message: String) : Exception(message) data class UnknownQueryTypeForRow(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "no filter type for $filterElementRow")
class FilterUnhandledEntityException(message : String) : Exception(message) }
class ConfigurationException(message: String) : Exception(message) {
}

@ -1,6 +1,7 @@
package net.pokeranalytics.android.model.filter package net.pokeranalytics.android.model.filter
import io.realm.RealmObject import io.realm.RealmModel
import net.pokeranalytics.android.model.realm.Session
/** /**
* We want to be able to store filters in the database: * We want to be able to store filters in the database:
@ -28,15 +29,37 @@ import io.realm.RealmObject
* *
*/ */
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.
*/ */
interface Filterable { interface Filterable : RealmModel {
/** /**
* return the path of the parameter used in the [QueryType] related to this entity * return the path of the parameter used in the [QueryType] related to this entity
*/ */
fun fieldNameForQueryType(queryType: QueryType) : String? fun fieldNameForQueryType(queryType: QueryType) : String?
}
class FilterHelper {
companion object {
inline fun <reified T : Filterable> fieldNameForQueryType(queryType: QueryType) : String? {
when (T::class) {
is Session -> {
Session.fieldNameForQueryType(queryType)
}
}
throw UnmanagedFilterField("Filterable type fields are not defined")
}
}
} }
// //

@ -1,12 +1,10 @@
package net.pokeranalytics.android.model.filter package net.pokeranalytics.android.model.filter
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmObject
import io.realm.RealmQuery import io.realm.RealmQuery
import net.pokeranalytics.android.exceptions.FilterValueMapException import net.pokeranalytics.android.exceptions.PokerAnalyticsException
import net.pokeranalytics.android.model.realm.FilterElementBlind
import net.pokeranalytics.android.model.realm.FilterElement import net.pokeranalytics.android.model.realm.FilterElement
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.FilterElementBlind
import java.util.* import java.util.*
@ -16,7 +14,7 @@ import java.util.*
* To handle that, the enum has a public [valueMap] variable * To handle that, the enum has a public [valueMap] variable
* A new type should also set the expected numericValues required in the [filterValuesExpectedKeys] * A new type should also set the expected numericValues required in the [filterValuesExpectedKeys]
*/ */
enum class QueryType(private var subType:SubType? = null) { enum class QueryType(var subType:SubType? = null) {
LIVE, LIVE,
CASH, CASH,
ONLINE, ONLINE,
@ -68,7 +66,7 @@ enum class QueryType(private var subType:SubType? = null) {
; ;
private enum class SubType { enum class SubType {
BETWEEN, BETWEEN,
MORE, MORE,
LESS; LESS;
@ -98,18 +96,19 @@ enum class QueryType(private var subType:SubType? = null) {
* main method of the enum * main method of the enum
* providing a base RealmQuery [realmQuery], the method is able to attached the corresponding query and returns the newly formed [RealmQuery] * providing a base RealmQuery [realmQuery], the method is able to attached the corresponding query and returns the newly formed [RealmQuery]
*/ */
fun filter(realmQuery: RealmQuery<out RealmObject>, filterable: Filterable): RealmQuery<out RealmObject> { inline fun <reified T : Filterable> filter(realmQuery: RealmQuery<T>): RealmQuery<T> {
when { when {
this == BLINDS -> { this == BLINDS -> {
val smallBlindFieldName = filterable.fieldNameForQueryType(SMALL_BLIND)
val bigBlindFieldName = filterable.fieldNameForQueryType(BIG_BLIND) val smallBlindFieldName = FilterHelper.fieldNameForQueryType<T>(SMALL_BLIND)
val currencyCodeFieldName = filterable.fieldNameForQueryType(CURRENCY_CODE) val bigBlindFieldName = FilterHelper.fieldNameForQueryType<T>(BIG_BLIND)
smallBlindFieldName ?: throw FilterValueMapException("fieldName is missing") val currencyCodeFieldName = FilterHelper.fieldNameForQueryType<T>(CURRENCY_CODE)
bigBlindFieldName ?: throw FilterValueMapException("fieldName is missing") smallBlindFieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown
currencyCodeFieldName ?: throw FilterValueMapException("fieldName is missing") bigBlindFieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown
currencyCodeFieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown
val blinds: RealmList<FilterElementBlind> by valueMap val blinds: RealmList<FilterElementBlind> by valueMap
blinds.forEachIndexed {index, blind -> blinds.forEachIndexed { index, blind ->
realmQuery realmQuery
.beginGroup() .beginGroup()
@ -137,12 +136,10 @@ enum class QueryType(private var subType:SubType? = null) {
} }
return realmQuery return realmQuery
} }
this == ONLINE -> return LIVE.filter(realmQuery.not(), filterable)
this == TOURNAMENT -> return CASH.filter(realmQuery.not(), filterable)
this == WEEK_DAY -> return WEEK_END.filter(realmQuery.not(), filterable)
else -> { else -> {
val fieldName = filterable.fieldNameForQueryType(this)
fieldName ?: throw FilterValueMapException("fieldName is missing") val fieldName = FilterHelper.fieldNameForQueryType<T>(this)
fieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown
this.subType?.let { subType -> this.subType?.let { subType ->
return when (subType) { return when (subType) {
@ -163,8 +160,8 @@ enum class QueryType(private var subType:SubType? = null) {
} }
return when (this) { return when (this) {
LIVE -> realmQuery.equalTo(fieldName, true) LIVE, ONLINE -> realmQuery.equalTo(fieldName, this == LIVE)
CASH -> realmQuery.equalTo(fieldName, Session.Type.CASH_GAME.ordinal) CASH, TOURNAMENT -> realmQuery.equalTo(fieldName, this.ordinal)
ALL_TOURNAMENT_FEATURES -> { ALL_TOURNAMENT_FEATURES -> {
val ids: Array<String> by valueMap val ids: Array<String> by valueMap
ids.forEach { ids.forEach {
@ -212,11 +209,13 @@ enum class QueryType(private var subType:SubType? = null) {
val year: Int by valueMap val year: Int by valueMap
realmQuery.equalTo(fieldName, year) realmQuery.equalTo(fieldName, year)
} }
WEEK_END -> { WEEK_END, WEEK_DAY -> {
realmQuery.`in`(fieldName, arrayOf(Calendar.SATURDAY, Calendar.SUNDAY)) var query = realmQuery.`in`(fieldName, arrayOf(Calendar.SATURDAY, Calendar.SUNDAY))
if (this == WEEK_DAY) { query.not() }
query
} }
else -> { else -> {
throw FilterValueMapException("filter type not handled") throw PokerAnalyticsException.QueryTypeUnhandled
} }
} }
} }
@ -267,7 +266,7 @@ enum class QueryType(private var subType:SubType? = null) {
valueMap = mapOf("year" to filterElement.year) valueMap = mapOf("year" to filterElement.year)
} }
else -> { else -> {
throw FilterValueMapException("filter type not handled") throw PokerAnalyticsException.QueryValueMapUnexpectedValue
} }
} }
} }
@ -278,10 +277,10 @@ enum class QueryType(private var subType:SubType? = null) {
field?.let { map -> field?.let { map ->
val missingKeys = map.keys.filter { !valueMapExceptedKeys.contains(it) } val missingKeys = map.keys.filter { !valueMapExceptedKeys.contains(it) }
if (map.keys.size == valueMapExceptedKeys.size && missingKeys.isNotEmpty()) { if (map.keys.size == valueMapExceptedKeys.size && missingKeys.isNotEmpty()) {
throw FilterValueMapException("valueMap does not contain $missingKeys") throw PokerAnalyticsException.QueryValueMapMissingKeys(missingKeys)
} }
} ?: run { } ?: run {
throw FilterValueMapException("valueMap null not expected") throw PokerAnalyticsException.QueryValueMapUnexpectedValue
} }
} }
return field return field

@ -2,8 +2,9 @@ package net.pokeranalytics.android.model.realm
import io.realm.* import io.realm.*
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.exceptions.FilterMissingEntityException import net.pokeranalytics.android.exceptions.PokerAnalyticsException
import net.pokeranalytics.android.exceptions.FilterUnhandledEntityException
import io.realm.kotlin.where
import net.pokeranalytics.android.model.filter.Filterable import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.QueryType import net.pokeranalytics.android.model.filter.QueryType
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow
@ -38,7 +39,7 @@ open class Filter(entity:Filterable) : RealmObject() {
fun filterableClass(entity: Filterable): FilterableClass { fun filterableClass(entity: Filterable): FilterableClass {
return when (entity) { return when (entity) {
is Session.Companion -> SESSION is Session.Companion -> SESSION
else -> throw FilterUnhandledEntityException("this entity is not filterable") else -> throw PokerAnalyticsException.FilterUnhandledEntity
} }
} }
} }
@ -64,11 +65,10 @@ open class Filter(entity:Filterable) : RealmObject() {
companion object { companion object {
@TestOnly @TestOnly
fun queryOn(realm: Realm, entity: Filterable, queries:List<QueryType>): RealmResults<*> { inline fun <reified T : Filterable> queryOn(realm: Realm, queries: List<QueryType>): RealmResults<T> {
val realmEntity : Class < out RealmObject > = FilterableClass.filterableClass(entity).relatedEntity var realmQuery = realm.where<T>()
var realmQuery : RealmQuery<out RealmObject> = realm.where(realmEntity)
queries.forEach { queries.forEach {
realmQuery = (it.filter(realmQuery, entity)) realmQuery = (it.filter<T>(realmQuery))
} }
return realmQuery.findAll() return realmQuery.findAll()
} }
@ -116,7 +116,7 @@ open class Filter(entity:Filterable) : RealmObject() {
fun countBy(filterCategoryRow: FilterCategoryRow) : Int { fun countBy(filterCategoryRow: FilterCategoryRow) : Int {
val sections = filterCategoryRow.filterSectionRows val sections = filterCategoryRow.filterSectionRows
return filterElements.count { return filterElements.count {
sections.contains(FilterSectionRow.valueOf(it.sectionName)) sections.contains(FilterSectionRow.valueOf(it.sectionName ?: throw PokerAnalyticsException.FilterElementUnknownSectionName))
} }
} }
@ -130,16 +130,15 @@ open class Filter(entity:Filterable) : RealmObject() {
return filterElementRow.contains(filtered) return filterElementRow.contains(filtered)
} }
fun results(): RealmResults<*> { inline fun <reified T : Filterable> results(): RealmResults<T> {
val filterableClass : FilterableClass = this.filterableClass ?: throw FilterMissingEntityException("this filter has no entity initialized") var realmQuery : RealmQuery<T> = realm.where<T>()
val realmEntity : Class < out RealmObject > = filterableClass.relatedEntity
var realmQuery : RealmQuery<out RealmObject> = realm.where(realmEntity)
this.filterElements.map { this.filterElements.map {
it.queryType it.queryType
}.forEach { }.forEach {
realmQuery = (it.filter(realmQuery, filterableClass.filterable)) realmQuery = it.filter(realmQuery)
} }
return realmQuery.findAll() return realmQuery.findAll()
} }
} }

@ -2,7 +2,7 @@ package net.pokeranalytics.android.model.realm
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmObject
import net.pokeranalytics.android.exceptions.FilterValueMapException import net.pokeranalytics.android.exceptions.PokerAnalyticsException
import net.pokeranalytics.android.model.filter.QueryType import net.pokeranalytics.android.model.filter.QueryType
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow.* import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow.*
@ -17,7 +17,8 @@ open class FilterElement() : RealmObject() {
} }
constructor(filterElementRows: ArrayList<FilterElementRow>) : this(filterElementRows.first().filterName, filterElementRows.first().filterSectionRow.name) { constructor(filterElementRows: ArrayList<FilterElementRow>) : this(filterElementRows.first().filterName, filterElementRows.first().filterSectionRow.name) {
this.stringValues = when (QueryType.valueOf(this.filterName)) { val filterName : String = this.filterName ?: throw PokerAnalyticsException.FilterElementUnknownName
this.stringValues = when (QueryType.valueOf(filterName)) {
QueryType.GAME, QueryType.BANKROLL, QueryType.TOURNAMENT_NAME, QueryType.ALL_TOURNAMENT_FEATURES, QueryType.ANY_TOURNAMENT_FEATURES, QueryType.LOCATION -> { QueryType.GAME, QueryType.BANKROLL, QueryType.TOURNAMENT_NAME, QueryType.ALL_TOURNAMENT_FEATURES, QueryType.ANY_TOURNAMENT_FEATURES, QueryType.LOCATION -> {
RealmList<String>().apply { RealmList<String>().apply {
this.addAll(filterElementRows.map { this.addAll(filterElementRows.map {
@ -86,11 +87,11 @@ open class FilterElement() : RealmObject() {
} }
} }
var filterName : String = "" var filterName : String? = null
var sectionName : String = "" var sectionName : String? = null
val queryType : QueryType val queryType : QueryType
get() = QueryType.valueOf(filterName) get() = QueryType.valueOf(this.filterName ?: throw PokerAnalyticsException.FilterElementUnknownName)
.apply { .apply {
this.updateValueMap(this@FilterElement) this.updateValueMap(this@FilterElement)
} }
@ -101,7 +102,7 @@ open class FilterElement() : RealmObject() {
private var blindValues : RealmList<FilterElementBlind>? = null private var blindValues : RealmList<FilterElementBlind>? = null
val ids : Array<String> val ids : Array<String>
get() = stringValues?.toTypedArray()?: throw FilterValueMapException("filter type not handled") get() = stringValues?.toTypedArray()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
val blinds : RealmList<FilterElementBlind> val blinds : RealmList<FilterElementBlind>
get() { get() {
@ -109,44 +110,44 @@ open class FilterElement() : RealmObject() {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
return it return it
} else { } else {
throw FilterValueMapException("filter is empty or null") throw PokerAnalyticsException.FilterElementExpectedValueMissing
} }
} }
throw FilterValueMapException("filter is empty or null") throw PokerAnalyticsException.FilterElementExpectedValueMissing
} }
val date : Date val date : Date
get() = dateValue?: throw FilterValueMapException("filter type not handled") get() = dateValue?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
val values : Array<Int> val values : Array<Int>
get() = numericValues?.map { get() = numericValues?.map {
it.toInt() it.toInt()
}?.toTypedArray()?: throw FilterValueMapException("filter type not handled") }?.toTypedArray()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
val value : Double val value : Double
get() = numericValues?.first()?: throw FilterValueMapException("filter type not handled") get() = numericValues?.first()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
val leftValue : Double val leftValue : Double
get() = numericValues?.first()?: throw FilterValueMapException("filter type not handled") get() = numericValues?.first()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
val rightValue : Double val rightValue : Double
get() = numericValues?.last()?: throw FilterValueMapException("filter type not handled") get() = numericValues?.last()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
val dayOfWeek : Int val dayOfWeek : Int
get() = numericValues?.first()?.toInt()?: throw FilterValueMapException("filter type not handled") get() = numericValues?.first()?.toInt()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
val month : Int val month : Int
get() = numericValues?.first()?.toInt()?: throw FilterValueMapException("filter type not handled") get() = numericValues?.first()?.toInt()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
val year : Int val year : Int
get() = numericValues?.first()?.toInt()?: throw FilterValueMapException("filter type not handled") get() = numericValues?.first()?.toInt()?: throw PokerAnalyticsException.FilterElementExpectedValueMissing
} }

@ -1,7 +1,7 @@
package net.pokeranalytics.android.ui.view.rowrepresentable package net.pokeranalytics.android.ui.view.rowrepresentable
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.FilterValueMapException import net.pokeranalytics.android.exceptions.PokerAnalyticsException
import net.pokeranalytics.android.model.filter.QueryType import net.pokeranalytics.android.model.filter.QueryType
import net.pokeranalytics.android.model.interfaces.Manageable import net.pokeranalytics.android.model.interfaces.Manageable
import net.pokeranalytics.android.model.realm.FilterElement import net.pokeranalytics.android.model.realm.FilterElement
@ -88,7 +88,7 @@ sealed class FilterElementRow : RowRepresentable {
is AllTournamentFeature -> QueryType.ALL_TOURNAMENT_FEATURES is AllTournamentFeature -> QueryType.ALL_TOURNAMENT_FEATURES
is ResultMoreThan -> QueryType.MORE_THAN_NET_RESULT is ResultMoreThan -> QueryType.MORE_THAN_NET_RESULT
is ResultLessThan -> QueryType.LESS_THAN_NET_RESULT is ResultLessThan -> QueryType.LESS_THAN_NET_RESULT
else -> throw FilterValueMapException("no filter type for $this") //TODO create exception else -> throw PokerAnalyticsException.UnknownQueryTypeForRow(this)
} }
} }

Loading…
Cancel
Save