Skip to content

Commit

Permalink
add duplicate filter to records filter
Browse files Browse the repository at this point in the history
  • Loading branch information
Razeeman committed Jan 12, 2025
1 parent 3aa17ff commit b641898
Show file tree
Hide file tree
Showing 44 changed files with 598 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ fun RecordsFilterParam.toModel(): RecordsFilter {
is RecordsFilterParam.DaysOfWeek -> RecordsFilter.DaysOfWeek(items)
is RecordsFilterParam.TimeOfDay -> RecordsFilter.TimeOfDay(range.toModel())
is RecordsFilterParam.Duration -> RecordsFilter.Duration(range.toModel())
is RecordsFilterParam.Duplications -> RecordsFilter.Duplications(items.map { it.toModel() })
}
}

Expand All @@ -142,6 +143,7 @@ fun RecordsFilter.toParams(): RecordsFilterParam {
is RecordsFilter.DaysOfWeek -> RecordsFilterParam.DaysOfWeek(items)
is RecordsFilter.TimeOfDay -> RecordsFilterParam.TimeOfDay(range.toParams())
is RecordsFilter.Duration -> RecordsFilterParam.Duration(range.toParams())
is RecordsFilter.Duplications -> RecordsFilterParam.Duplications(items.map { it.toParams() })
}
}

Expand Down Expand Up @@ -189,6 +191,20 @@ fun RecordsFilter.TagItem.toParams(): RecordsFilterParam.TagItem {
}
}

fun RecordsFilterParam.DuplicationsItem.toModel(): RecordsFilter.DuplicationsItem {
return when (this) {
is RecordsFilterParam.DuplicationsItem.SameActivity -> RecordsFilter.DuplicationsItem.SameActivity
is RecordsFilterParam.DuplicationsItem.SameTimes -> RecordsFilter.DuplicationsItem.SameTimes
}
}

fun RecordsFilter.DuplicationsItem.toParams(): RecordsFilterParam.DuplicationsItem {
return when (this) {
is RecordsFilter.DuplicationsItem.SameActivity -> RecordsFilterParam.DuplicationsItem.SameActivity
is RecordsFilter.DuplicationsItem.SameTimes -> RecordsFilterParam.DuplicationsItem.SameTimes
}
}

fun RangeLengthParams.toModel(): RangeLength {
return when (this) {
is RangeLengthParams.Day -> RangeLength.Day
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import com.example.util.simpletimetracker.domain.recordType.interactor.RecordTyp
import com.example.util.simpletimetracker.domain.record.interactor.RunningRecordInteractor
import com.example.util.simpletimetracker.domain.record.mapper.RangeMapper
import com.example.util.simpletimetracker.domain.daysOfWeek.model.DayOfWeek
import com.example.util.simpletimetracker.domain.record.extension.getDuplicationItems
import com.example.util.simpletimetracker.domain.record.extension.hasDuplicationsFilter
import com.example.util.simpletimetracker.domain.record.interactor.GetDuplicatedRecordsInteractor
import com.example.util.simpletimetracker.domain.record.interactor.GetUntrackedRecordsInteractor
import com.example.util.simpletimetracker.domain.record.model.Range
import com.example.util.simpletimetracker.domain.statistics.model.RangeLength
Expand All @@ -49,6 +52,7 @@ class RecordFilterInteractor @Inject constructor(
private val runningRecordInteractor: RunningRecordInteractor,
private val getUntrackedRecordsInteractor: GetUntrackedRecordsInteractor,
private val getMultitaskRecordsInteractor: GetMultitaskRecordsInteractor,
private val getDuplicatedRecordsInteractor: GetDuplicatedRecordsInteractor,
private val timeMapper: TimeMapper,
private val rangeMapper: RangeMapper,
private val prefsInteractor: PrefsInteractor,
Expand Down Expand Up @@ -104,10 +108,11 @@ class RecordFilterInteractor @Inject constructor(
val filteredTagItems: List<RecordsFilter.TagItem> = filters.getFilteredTags()
val filteredTaggedIds: List<Long> = filteredTagItems.getTaggedIds()
val filteredUntagged: Boolean = filteredTagItems.hasUntaggedItem()
val manuallyFilteredIds: List<Long> = filters.getManuallyFilteredRecordIds()
val manuallyFilteredIds: Map<Long, Boolean> = filters.getManuallyFilteredRecordIds()
val daysOfWeek: List<DayOfWeek> = filters.getDaysOfWeek()
val timeOfDay: Range? = filters.getTimeOfDay()
val durations: List<Range> = filters.getDuration()?.let(::listOf).orEmpty()
val duplicationItems: List<RecordsFilter.DuplicationsItem> = filters.getDuplicationItems()

// TODO Use different queries for optimization.
// TODO by tag (tagged, untagged).
Expand Down Expand Up @@ -166,6 +171,17 @@ class RecordFilterInteractor @Inject constructor(
}
}

val duplicationIds: Map<Long, Boolean> = if (filters.hasDuplicationsFilter()) {
getDuplicatedRecordsInteractor.execute(
filters = duplicationItems,
records = records,
).let {
it.original + it.duplications
}.associateWith { true }
} else {
emptyMap()
}

// TODO multitask filters.

fun RecordBase.selectedByActivity(): Boolean {
Expand Down Expand Up @@ -211,6 +227,12 @@ class RecordFilterInteractor @Inject constructor(
return id in manuallyFilteredIds
}

fun RecordBase.selectedByDuplications(): Boolean {
if (duplicationItems.isEmpty()) return true
if (this !is Record) return true
return id in duplicationIds
}

fun RecordBase.selectedByDayOfWeek(): Boolean {
if (daysOfWeek.isEmpty()) return true

Expand Down Expand Up @@ -292,7 +314,8 @@ class RecordFilterInteractor @Inject constructor(
!record.isManuallyFiltered() &&
record.selectedByDayOfWeek() &&
record.selectedByTimeOfDay() &&
record.selectedByDuration()
record.selectedByDuration() &&
record.selectedByDuplications()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ fun <T> MutableSet<T>.addOrRemove(item: T) {
if (item in this) remove(item) else add(item)
}

fun <T, U> MutableMap<T, U>.addOrRemove(item: T, value: U) {
if (item in this) remove(item) else put(item, value)
}

operator fun <T> MutableCollection<in T>.plusAssign(element: T?) {
if (element != null) this.add(element)
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,11 @@ fun List<RecordsFilter>.getFilteredTags(): List<RecordsFilter.TagItem> {
.flatten()
}

fun List<RecordsFilter>.getManuallyFilteredRecordIds(): List<Long> {
fun List<RecordsFilter>.getManuallyFilteredRecordIds(): Map<Long, Boolean> {
return filterIsInstance<RecordsFilter.ManuallyFiltered>()
.map(RecordsFilter.ManuallyFiltered::recordIds)
.flatten()
.associateWith { true }
}

fun List<RecordsFilter>.getDaysOfWeek(): List<DayOfWeek> {
Expand Down Expand Up @@ -159,3 +160,21 @@ fun List<RecordsFilter.CategoryItem>.hasUncategorizedItem(): Boolean {
fun List<RecordsFilter>.hasManuallyFiltered(): Boolean {
return any { it is RecordsFilter.ManuallyFiltered }
}

fun List<RecordsFilter>.hasDuplicationsFilter(): Boolean {
return any { it is RecordsFilter.Duplications }
}

fun List<RecordsFilter>.getDuplicationItems(): List<RecordsFilter.DuplicationsItem> {
return filterIsInstance<RecordsFilter.Duplications>()
.map(RecordsFilter.Duplications::items)
.flatten()
}

fun List<RecordsFilter.DuplicationsItem>.hasSameActivity(): Boolean {
return any { it is RecordsFilter.DuplicationsItem.SameActivity }
}

fun List<RecordsFilter.DuplicationsItem>.hasSameTimes(): Boolean {
return any { it is RecordsFilter.DuplicationsItem.SameTimes }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.example.util.simpletimetracker.domain.record.interactor

import com.example.util.simpletimetracker.domain.record.extension.hasSameActivity
import com.example.util.simpletimetracker.domain.record.model.Record
import com.example.util.simpletimetracker.domain.record.model.RecordBase
import com.example.util.simpletimetracker.domain.record.model.RecordsFilter
import javax.inject.Inject

class GetDuplicatedRecordsInteractor @Inject constructor() {

fun execute(
filters: List<RecordsFilter.DuplicationsItem>,
records: List<RecordBase>,
): Result {
if (filters.isEmpty()) {
return Result(
original = emptyList(),
duplications = emptyList(),
)
}
val hasSameActivity = filters.hasSameActivity()

data class Id(
val typeId: Long,
val timeStarted: Long,
val timeEnded: Long,
)

val data = mutableMapOf<Id, MutableList<Record>>()
val result = mutableListOf<Long>()
val resultDuplications = mutableListOf<Long>()

// Check duplications by adding to map with data class as key.
records.forEach { record ->
if (record !is Record) return@forEach
val id = Id(
typeId = if (hasSameActivity) {
record.typeIds.firstOrNull() ?: return@forEach
} else {
0L
},
// Times are always checked and should be in the list.
timeStarted = record.timeStarted,
timeEnded = record.timeEnded,
)
data[id] = data.getOrElse(key = id, defaultValue = { mutableListOf() })
.apply { add(record) }
}

data.forEach { (_, duplications) ->
if (duplications.size < 2) return@forEach
// This record will not be counted as duplication.
val originalRecord = duplications.firstOrNull {
it.tagIds.isNotEmpty() ||
it.comment.isNotEmpty()
} ?: duplications.firstOrNull()
duplications.forEach { record ->
if (record.id == originalRecord?.id) {
result += record.id
} else {
resultDuplications += record.id
}
}
}

return Result(
original = result,
duplications = resultDuplications,
)
}

data class Result(
val original: List<Long>,
val duplications: List<Long>,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ sealed interface RecordsFilter {

data class Duration(val range: Range) : RecordsFilter // duration-from, duration-to in range.

data class Duplications(val items: List<DuplicationsItem>) : RecordsFilter

sealed interface CommentItem {
object NoComment : CommentItem
object AnyComment : CommentItem
Expand All @@ -45,4 +47,9 @@ sealed interface RecordsFilter {
data class Tagged(val tagId: Long) : TagItem
object Untagged : TagItem
}

sealed interface DuplicationsItem {
object SameActivity : DuplicationsItem
object SameTimes : DuplicationsItem
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,13 @@ class DataEditViewModel @Inject constructor(
RecordsFilterParams(
tag = FILTER_TAG,
title = resourceRepo.getString(R.string.chart_filter_hint),
dateSelectionAvailable = true,
untrackedSelectionAvailable = false,
multitaskSelectionAvailable = false,
addRunningRecords = false,
flags = RecordsFilterParams.Flags(
dateSelectionAvailable = true,
untrackedSelectionAvailable = false,
multitaskSelectionAvailable = false,
duplicationsSelectionAvailable = true,
addRunningRecords = false,
),
filters = filters.map(RecordsFilter::toParams),
defaultLastDaysNumber = 7,
).let(router::navigate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ data class RecordsFilterButtonViewData(

enum class Type {
INVERT_SELECTION,
FILTER_DUPLICATES,
}
}
Loading

0 comments on commit b641898

Please sign in to comment.