diff --git a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/DateFilterInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/DateFilterInstrumentedUnitTest.kt index a09ba699..46782e1b 100644 --- a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/DateFilterInstrumentedUnitTest.kt +++ b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/DateFilterInstrumentedUnitTest.kt @@ -8,6 +8,7 @@ import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.FilterCondition import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow +import net.pokeranalytics.android.util.extensions.hourMinute import net.pokeranalytics.android.util.extensions.startOfDay import org.junit.Assert import org.junit.Test @@ -481,4 +482,39 @@ class DateFilterInstrumentedUnitTest : BaseFilterInstrumentedUnitTest() { Assert.assertEquals(s1.id, (this).id) } } + + @Test + fun testFomTimeToTime() { + + val realm = this.mockRealm + realm.beginTransaction() + + val cal = Calendar.getInstance() // creates calendar + cal.time = Date() // sets calendar time/date + cal.set(Calendar.HOUR_OF_DAY, 14) // adds one hour + + println("<<<<< s1 ${cal.hourMinute()}") + val s1 = Session.testInstance(100.0, false, cal.time, 1) + println("<<<<< s1 ${cal.hourMinute()}") + cal.add(Calendar.HOUR_OF_DAY, 2) // adds one hour + println("<<<<< s2 ${cal.hourMinute()}") + val s2 = Session.testInstance(100.0, true, cal.time, 1) + println("<<<<< s2 ${cal.hourMinute()}") + + cal.set(Calendar.HOUR_OF_DAY, 23) // adds one hour + println("<<<<< s3 ${cal.hourMinute()}") + val s3 = Session.testInstance(100.0, true, cal.time, 2) + println("<<<<< s3 ${cal.hourMinute()}") + + realm.commitTransaction() + + val filter = QueryCondition.StartedFromTime(s2.startDate!!) + val filter2 = QueryCondition.EndedToTime(s2.endDate!!) + val sessions = Filter.queryOn(realm, Query(filter, filter2)) + + Assert.assertEquals(1, sessions.size) + sessions[0]?.run { + Assert.assertEquals(s2.id, (this).id) + } + } } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt b/app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt index 0e4d9104..2b493d0d 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt @@ -47,16 +47,26 @@ class Query { } } -// val name: String -// get() { -// return this._conditions.joinToString(" : ") { it.getDisplayName() } -// } - inline fun queryWith(query: RealmQuery): RealmQuery { var realmQuery = query - this.conditions.forEach { - realmQuery = it.queryWith(realmQuery) + + val queryFromTime = this.conditions.filter { + it is QueryCondition.StartedFromTime + }.firstOrNull() + val queryToTime = this.conditions.filter { + it is QueryCondition.EndedToTime + }.firstOrNull() + + this.conditions.forEach { + if (it is QueryCondition.StartedFromTime) { + realmQuery = it.queryWith(realmQuery, queryToTime) + } else if (it is QueryCondition.EndedToTime) { + realmQuery = it.queryWith(realmQuery, queryFromTime) + } else { + realmQuery = it.queryWith(realmQuery) + } } + return realmQuery } diff --git a/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt b/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt index 619d5205..63f246b8 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt @@ -21,6 +21,7 @@ import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.UserDefaults import net.pokeranalytics.android.util.extensions.endOfDay +import net.pokeranalytics.android.util.extensions.hourMinute import net.pokeranalytics.android.util.extensions.startOfDay import net.pokeranalytics.android.util.extensions.toCurrency import java.text.DateFormatSymbols @@ -87,6 +88,14 @@ sealed class QueryCondition : FilterElementRow { val baseId = this::class.simpleName ?: throw PokerAnalyticsException.FilterElementUnknownName + val groupId: String + get() { + when (this.operator) { + Operator.MORE, Operator.LESS -> return "${this.operator.name.toLowerCase().capitalize()}$baseId" + } + return baseId + } + val id: List get() { when (this.operator) { Operator.MORE, Operator.LESS -> return listOf("$baseId+${this.operator.name}") @@ -164,10 +173,14 @@ sealed class QueryCondition : FilterElementRow { override fun labelForValue(value: Date, context: Context): String { return value.toString() } + override var listOfValues = ArrayList() override var singleValue: Date get() { return listOfValues.firstOrNull() ?: Date() } - set(value) { listOfValues.add(value) } + set(value) { + listOfValues.removeAll(this.listOfValues) + listOfValues.add(value) + } override fun updateValueBy(filterCondition: FilterCondition) { super.updateValueBy(filterCondition) @@ -181,7 +194,10 @@ sealed class QueryCondition : FilterElementRow { } override var singleValue: Int get() { return listOfValues.firstOrNull() ?: 0 } - set(value) { listOfValues.add(value) } + set(value) { + listOfValues.removeAll(this.listOfValues) + listOfValues.add(value) + } override fun updateValueBy(filterCondition: FilterCondition) { super.updateValueBy(filterCondition) @@ -398,23 +414,23 @@ sealed class QueryCondition : FilterElementRow { class Duration: SingleInt() { var minutes:Int get() { return singleValue } - set(value) { singleValue = value } + set(value) { listOfValues = arrayListOf(value) } override val viewType: Int = RowViewType.TITLE_VALUE_CHECK.ordinal override val bottomSheetType: BottomSheetType = BottomSheetType.DOUBLE_EDIT_TEXT } - class StartedFromTime: TimeQuery() { + class StartedFromTime(startTime:Date = Date().startOfDay()): TimeQuery() { override var operator = Operator.MORE init { - this.singleValue = Date().startOfDay() + this.listOfValues = arrayListOf(startTime) } } - class EndedToTime: TimeQuery() { + class EndedToTime(endTime:Date = Date().endOfDay()): TimeQuery() { override var operator = Operator.LESS init { - this.singleValue = Date().endOfDay() + this.listOfValues = arrayListOf(endTime) } } @@ -422,7 +438,7 @@ sealed class QueryCondition : FilterElementRow { * 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] */ - inline fun queryWith(realmQuery: RealmQuery): RealmQuery { + inline fun queryWith(realmQuery: RealmQuery, otherQueryCondition:QueryCondition? = null): RealmQuery { val fieldName = FilterHelper.fieldNameForQueryType(this::class.java) fieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown @@ -430,54 +446,74 @@ sealed class QueryCondition : FilterElementRow { //is Between -> realmQuery.between(fieldName, leftValue, rightValue) //is BetweenLeftExclusive -> realmQuery.greaterThan(fieldName, leftValue).and().lessThanOrEqualTo(fieldName, rightValue) //is BetweenRightExclusive -> realmQuery.greaterThanOrEqualTo(fieldName, leftValue).and().lessThan(fieldName, rightValue) - IsLive, IsOnline -> return realmQuery.equalTo(fieldName, this == IsLive) - IsCash -> return realmQuery.equalTo(fieldName, Session.Type.CASH_GAME.ordinal) - IsTournament -> return realmQuery.equalTo(fieldName, Session.Type.TOURNAMENT.ordinal) - IsWeekEnd, IsWeekDay -> { + is IsLive, is IsOnline -> return realmQuery.equalTo(fieldName, this == IsLive) + is IsCash -> return realmQuery.equalTo(fieldName, Session.Type.CASH_GAME.ordinal) + is IsTournament -> return realmQuery.equalTo(fieldName, Session.Type.TOURNAMENT.ordinal) + is IsWeekEnd, is IsWeekDay -> { var query = realmQuery if (this == IsWeekDay) { query = realmQuery.not() } return query.`in`(fieldName, arrayOf(Calendar.SATURDAY, Calendar.SUNDAY)) } - IsToday -> { + is IsToday -> { val startDate = Date() return realmQuery.greaterThanOrEqualTo(fieldName, startDate.startOfDay()).and().lessThanOrEqualTo(fieldName, startDate.endOfDay()) } - WasTodayAndYesterday-> { + is WasTodayAndYesterday -> { val startDate = Date() val calendar = Calendar.getInstance() calendar.time = startDate calendar.add(Calendar.HOUR_OF_DAY, -24) return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and().lessThanOrEqualTo(fieldName, startDate.endOfDay()) } - WasYesterday -> { + is WasYesterday -> { val calendar = Calendar.getInstance() calendar.time = Date() calendar.add(Calendar.HOUR_OF_DAY, -24) return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and().lessThanOrEqualTo(fieldName, calendar.time.endOfDay()) } - DuringThisWeek -> { + is DuringThisWeek -> { val startDate = Date() val calendar = Calendar.getInstance() calendar.time = startDate calendar.set(Calendar.DAY_OF_WEEK_IN_MONTH, Calendar.SUNDAY) return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and().lessThanOrEqualTo(fieldName, startDate.endOfDay()) } - DuringThisMonth -> { + is DuringThisMonth -> { val startDate = Date() val calendar = Calendar.getInstance() calendar.time = startDate calendar.set(Calendar.DAY_OF_MONTH, 1) return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and().lessThanOrEqualTo(fieldName, startDate.endOfDay()) } - DuringThisYear -> { + is DuringThisYear -> { val startDate = Date() val calendar = Calendar.getInstance() calendar.time = startDate calendar.set(Calendar.DAY_OF_YEAR, 1) return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and().lessThanOrEqualTo(fieldName, startDate.endOfDay()) } + is StartedFromTime -> { + val calendar = Calendar.getInstance() + calendar.time = singleValue + realmQuery.greaterThanOrEqualTo(fieldName, calendar.hourMinute()) + if (otherQueryCondition is EndedToTime) { + calendar.time = otherQueryCondition.singleValue + realmQuery.lessThanOrEqualTo(fieldName, calendar.hourMinute()) + } + return realmQuery + } + is EndedToTime -> { + val calendar = Calendar.getInstance() + calendar.time = singleValue + realmQuery.lessThanOrEqualTo(fieldName, calendar.hourMinute()) + if (otherQueryCondition is StartedFromTime) { + calendar.time = otherQueryCondition.singleValue + realmQuery.greaterThanOrEqualTo(fieldName, calendar.hourMinute()) + } + return realmQuery + } } return when (operator) { diff --git a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt index d10af366..9e300e87 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt @@ -112,6 +112,7 @@ class PokerAnalyticsMigration : RealmMigration { it.addField("duplicateValue", Boolean::class.java) it.addRealmListField("entries", CustomFieldEntry::class.java) } + schema.get("ReportSetup")?.let { it.addRealmListField("statIds", Int::class.java) it.addRealmListField("criteriaIds", Int::class.java) @@ -120,6 +121,11 @@ class PokerAnalyticsMigration : RealmMigration { it.addRealmObjectField("filter", filterSchema) } } + + schema.get("Session")?.let { + it.addField("startDateHourMinuteComponent", Double::class.java).setNullable("startDateHourMinuteComponent", true) + it.addField("endDateHourMinuteComponent", Double::class.java).setNullable("endDateHourMinuteComponent", true) + } currentVersion++ } } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt index a9071b0e..8baf5a00 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt @@ -9,7 +9,6 @@ import net.pokeranalytics.android.model.filter.Query import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow -import timber.log.Timber import java.util.* /** @@ -33,15 +32,12 @@ open class Filter : RealmObject(), RowRepresentable { } inline fun queryOn(realm: Realm, query: Query, sortField: String? = null): RealmResults { - var realmQuery = realm.where() - query.conditions.forEach { - realmQuery = it.queryWith(realmQuery) - } + val realmQuery = realm.where() sortField?.let { - realmQuery.sort(it) - } - Timber.d(">>> Filter query: ${realmQuery.description}") - return realmQuery.findAll() + return query.queryWith(realmQuery).sort(it).findAll() + } ?: run { + return query.queryWith(realmQuery).findAll() + } } fun sortedByUsage(realm: Realm): RealmResults { @@ -68,13 +64,13 @@ open class Filter : RealmObject(), RowRepresentable { println("list of querys previous: ${this.filterConditions.map { it.queryCondition.id }}") filterConditionRows .map { - it.filterSectionRow + it.groupId } .distinct() - .forEach { filterName-> + .forEach { groupId-> filterConditionRows .filter { - it.filterSectionRow == filterName + it.groupId == groupId } .apply { @@ -83,7 +79,7 @@ open class Filter : RealmObject(), RowRepresentable { casted.addAll(this) val newFilterCondition = FilterCondition(casted) val previousCondition = filterConditions.filter { - it.filterName == newFilterCondition.filterName + it.filterName == newFilterCondition.filterName && it.operator == newFilterCondition.operator } filterConditions.removeAll(previousCondition) filterConditions.add(newFilterCondition) @@ -126,22 +122,19 @@ open class Filter : RealmObject(), RowRepresentable { } inline fun results(firstField: String? = null, secondField: String? = null): RealmResults { - var realmQuery = realm.where() - this.filterConditions.map { - it.queryCondition - }.forEach { - realmQuery = it.queryWith(realmQuery) - } + + val realmQuery = realm.where() if (firstField != null && secondField != null) { - return realmQuery.distinct(firstField, secondField).findAll() + return this.query.queryWith(realmQuery).distinct(firstField, secondField).findAll() } if (firstField != null) { - return realmQuery.distinct(firstField).findAll() + return this.query.queryWith(realmQuery).distinct(firstField).findAll() } - return realmQuery.findAll() - } + + return this.query.queryWith(realmQuery).findAll() + } val query: Query get() { diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt index c91d5b8b..56d5b759 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt @@ -92,6 +92,9 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat AnyMonthOfYear::class.java -> "month" AnyYear::class.java -> "year" IsToday::class.java, WasYesterday::class.java, WasTodayAndYesterday::class.java, DuringThisYear::class.java, DuringThisMonth::class.java, DuringThisWeek::class.java -> "startDate" + StartedFromTime::class.java -> "startDateHourMinuteComponent" + EndedToTime::class.java -> "endDateHourMinuteComponent" + Duration::class.java -> "netDuration" else -> null } } @@ -122,12 +125,39 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat override var year: Int? = null override var dayOfMonth: Int? = null + private var startDateHourMinuteComponent: Double? = null + get() { + if (field == null && startDate != null) { + val cal = Calendar.getInstance() + cal.time = startDate + field = cal.hourMinute() + } + return field + } + + private var endDateHourMinuteComponent: Double? = null + get() { + if (field == null && endDate != null) { + val cal = Calendar.getInstance() + cal.time = endDate + field = cal.hourMinute() + } + return field + } + /** * The start date of the session */ var startDate: Date? = null set(value) { field = value + if (field == null) { + startDateHourMinuteComponent = null + } else { + val cal = Calendar.getInstance() + cal.time = field + startDateHourMinuteComponent = cal.hourMinute() + } this.updateTimeParameter(field) this.computeNetDuration() @@ -146,7 +176,15 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat var endDate: Date? = null set(value) { field = value - this.computeNetDuration() + if (field == null) { + endDateHourMinuteComponent = null + } else { + val cal = Calendar.getInstance() + cal.time = field + endDateHourMinuteComponent = cal.hourMinute() + } + + this.computeNetDuration() this.dateChanged() this.defineDefaultTournamentBuyinIfNecessary() this.computeStats() diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt index 044b7ad9..bb3adb40 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt @@ -63,6 +63,7 @@ open class FilterDetailsFragment : RealmFragment(), StaticRowRepresentableDataS override fun onBackPressed() { super.onBackPressed() + println("<<<<< back pressed") saveData() } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt index e485a5e0..9aeb056d 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt @@ -11,7 +11,6 @@ import kotlinx.android.synthetic.main.fragment_editable_data.* import kotlinx.android.synthetic.main.fragment_filters.view.* import net.pokeranalytics.android.R import net.pokeranalytics.android.model.realm.Filter -import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.ui.activity.FilterDetailsActivity import net.pokeranalytics.android.ui.activity.FiltersActivity import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity @@ -93,7 +92,7 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource, override fun onOptionsItemSelected(item: MenuItem?): Boolean { when (item!!.itemId) { - R.id.save -> validUpdates() + R.id.save -> validateUpdates() R.id.delete -> deleteFilter() } return true @@ -185,10 +184,10 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource, } /** - * Valid the updates of the queryWith + * Validate the updates of the queryWith */ - private fun validUpdates() { - Timber.d("Valid queryWith updates") + private fun validateUpdates() { + Timber.d("Validate queryWith updates") val filterId = currentFilter?.id ?: "" finishActivityWithResult(filterId) } diff --git a/app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt b/app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt index 7c905504..519cdd94 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt @@ -7,6 +7,12 @@ import java.util.* // Calendar +// Return a double representing the hour / minute of a date from a calendar +fun Calendar.hourMinute(): Double { + return (this.get(Calendar.HOUR_OF_DAY) + this.get(Calendar.MINUTE).toDouble()/60.0).roundOffDecimal() +} + + // Return if the calendar dates are in the same month fun Calendar.isSameMonth(calendar: Calendar): Boolean { return calendar.get(Calendar.YEAR) == this.get(Calendar.YEAR) && diff --git a/app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt b/app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt index c2578945..1313248c 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt @@ -3,6 +3,7 @@ package net.pokeranalytics.android.util.extensions import android.content.Context import net.pokeranalytics.android.R import java.lang.Math.abs +import java.math.RoundingMode import java.text.DecimalFormat import java.text.NumberFormat import java.util.* @@ -36,6 +37,12 @@ fun Double.round(): String { return formatter.format(this) } +fun Double.roundOffDecimal(): Double { + val df = DecimalFormat("#.##") + df.roundingMode = RoundingMode.CEILING + return df.format(this).toDouble() +} + fun Double.formatted(): String { val format = NumberFormat.getNumberInstance() format.maximumFractionDigits = 2