Skip to content

Commit

Permalink
add count goals to excess deficit graph on detailed statistics
Browse files Browse the repository at this point in the history
  • Loading branch information
Razeeman committed Dec 21, 2024
1 parent f166fd9 commit deed522
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ class RecordTypeViewDataMapper @Inject constructor(
val valueLeft = goalValue - current
val isLimit = goal?.subtype == RecordTypeGoal.Subtype.Limit

// TODO GOAL excess for goal count?
// TODO GOAL detailed stats, excess graph, count deficit when should have a goal.
// TODO GOAL streaks, skip count days when should not have a goal (daily goals).
return if (goal != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,6 @@ class RangeMapper @Inject constructor() {
return ranges.sumOf(Range::duration)
}

fun mapRecordsToDuration(records: List<RecordBase>): Long {
return records.sumOf(RecordBase::duration)
}

private fun clampNormalRecordToRange(
record: Record,
range: Range,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.example.util.simpletimetracker.feature_statistics_detail.model.ChartB
import com.example.util.simpletimetracker.feature_statistics_detail.model.ChartBarDataRange
import com.example.util.simpletimetracker.feature_statistics_detail.model.ChartGrouping
import com.example.util.simpletimetracker.feature_statistics_detail.model.ChartLength
import com.example.util.simpletimetracker.feature_statistics_detail.model.ChartMode
import com.example.util.simpletimetracker.feature_statistics_detail.model.ChartSplitSortMode
import com.example.util.simpletimetracker.feature_statistics_detail.viewData.StatisticsDetailChartCompositeViewData
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -95,6 +96,7 @@ class StatisticsDetailChartInteractor @Inject constructor(
val typesOrder = types.map(RecordType::id)
val canSplitByActivity = canSplitByActivity(filter)
val canComparisonSplitByActivity = canSplitByActivity(compare)
val chartMode = ChartMode.DURATIONS

val compositeData = getChartRangeSelectionData(
currentChartGrouping = currentChartGrouping,
Expand All @@ -116,6 +118,7 @@ class StatisticsDetailChartInteractor @Inject constructor(
typesOrder = typesOrder,
typesMap = typesMap,
isDarkTheme = isDarkTheme,
chartMode = chartMode,
splitByActivity = splitByActivity && canSplitByActivity,
splitSortMode = splitSortMode,
)
Expand All @@ -130,6 +133,7 @@ class StatisticsDetailChartInteractor @Inject constructor(
typesOrder = typesOrder,
typesMap = typesMap,
isDarkTheme = isDarkTheme,
chartMode = chartMode,
splitSortMode = splitSortMode,
)
val compareData = getChartData(
Expand All @@ -138,6 +142,7 @@ class StatisticsDetailChartInteractor @Inject constructor(
typesOrder = typesOrder,
typesMap = typesMap,
isDarkTheme = isDarkTheme,
chartMode = chartMode,
splitByActivity = splitByActivity && canComparisonSplitByActivity,
splitSortMode = splitSortMode,
)
Expand All @@ -164,45 +169,34 @@ class StatisticsDetailChartInteractor @Inject constructor(
appliedChartGrouping = compositeData.appliedChartGrouping,
availableChartLengths = compositeData.availableChartLengths,
appliedChartLength = compositeData.appliedChartLength,
chartMode = chartMode,
useProportionalMinutes = useProportionalMinutes,
showSeconds = showSeconds,
isDarkTheme = isDarkTheme,
)
}

fun getGoalValue(
goals: List<RecordTypeGoal>,
appliedChartGrouping: ChartGrouping,
): Long {
return getGoal(
goals = goals,
appliedChartGrouping = appliedChartGrouping,
).value * 1000
}

fun getGoalSubtype(
goals: List<RecordTypeGoal>,
appliedChartGrouping: ChartGrouping,
): RecordTypeGoal.Subtype {
return getGoal(
goals = goals,
appliedChartGrouping = appliedChartGrouping,
)?.subtype ?: RecordTypeGoal.Subtype.Goal
}

fun getChartData(
allRecords: List<RecordBase>,
ranges: List<ChartBarDataRange>,
typesOrder: List<Long>,
typesMap: Map<Long, RecordType>,
isDarkTheme: Boolean,
chartMode: ChartMode,
splitByActivity: Boolean,
splitSortMode: ChartSplitSortMode,
): List<ChartBarDataDuration> {
fun mapEmpty(): List<ChartBarDataDuration> {
return ranges.map { ChartBarDataDuration(legend = it.legend, durations = listOf(0L to 0)) }
}

fun mapRangesToValue(list: List<Range>): Long {
return when (chartMode) {
ChartMode.DURATIONS -> rangeMapper.mapToDuration(list)
ChartMode.COUNTS -> list.size.toLong()
}
}

val unknownColor = colorMapper.toUntrackedColor(isDarkTheme)

val records = rangeMapper.getRecordsFromRange(
Expand All @@ -221,15 +215,15 @@ class StatisticsDetailChartInteractor @Inject constructor(
val durations = if (!splitByActivity) {
rangeMapper.getRecordsFromRange(records, range)
.map { record -> rangeMapper.clampToRange(record, range) }
.let(rangeMapper::mapToDuration)
.let(::mapRangesToValue)
.let { listOf(it to 0) }
} else {
rangeMapper.getRecordsFromRange(records, range)
.groupBy { it.typeIds.firstOrNull().orZero() }
.toList()
.map { (id, records) ->
val value = records.map { record -> rangeMapper.clampToRange(record, range) }
.let(rangeMapper::mapToDuration)
.let(::mapRangesToValue)
value to id
}
.run {
Expand Down Expand Up @@ -381,6 +375,7 @@ class StatisticsDetailChartInteractor @Inject constructor(
typesOrder: List<Long>,
typesMap: Map<Long, RecordType>,
isDarkTheme: Boolean,
chartMode: ChartMode,
splitSortMode: ChartSplitSortMode,
): List<ChartBarDataDuration> {
return if (rangeLength != RangeLength.All) {
Expand All @@ -399,6 +394,7 @@ class StatisticsDetailChartInteractor @Inject constructor(
typesMap = typesMap,
isDarkTheme = isDarkTheme,
splitByActivity = false,
chartMode = chartMode,
splitSortMode = splitSortMode,
)
} else {
Expand Down Expand Up @@ -563,6 +559,17 @@ class StatisticsDetailChartInteractor @Inject constructor(
filter.getTypeIds().size > 1
}

private fun getGoalValue(
goals: List<RecordTypeGoal>,
appliedChartGrouping: ChartGrouping,
): Long {
// Currently only duration goals are on chart.
return getGoal(
goals = goals,
appliedChartGrouping = appliedChartGrouping,
).value * 1000
}

private fun getGoal(
goals: List<RecordTypeGoal>,
appliedChartGrouping: ChartGrouping,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.example.util.simpletimetracker.feature_statistics_detail.interactor

import com.example.util.simpletimetracker.domain.extension.getDaily
import com.example.util.simpletimetracker.domain.extension.getMonthly
import com.example.util.simpletimetracker.domain.extension.getWeekly
import com.example.util.simpletimetracker.domain.extension.value
import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor
import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor
import com.example.util.simpletimetracker.domain.model.DayOfWeek
Expand All @@ -12,6 +16,7 @@ import com.example.util.simpletimetracker.feature_statistics_detail.interactor.S
import com.example.util.simpletimetracker.feature_statistics_detail.mapper.StatisticsDetailViewDataMapper
import com.example.util.simpletimetracker.feature_statistics_detail.model.ChartGrouping
import com.example.util.simpletimetracker.feature_statistics_detail.model.ChartLength
import com.example.util.simpletimetracker.feature_statistics_detail.model.ChartMode
import com.example.util.simpletimetracker.feature_statistics_detail.model.ChartSplitSortMode
import com.example.util.simpletimetracker.feature_statistics_detail.viewData.StatisticsDetailGoalsCompositeViewData
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -53,7 +58,15 @@ class StatisticsDetailGoalsInteractor @Inject constructor(
firstDayOfWeek = firstDayOfWeek,
goals = goals,
)

val goal = getGoal(
goals = goals,
appliedChartGrouping = compositeData.appliedChartGrouping,
)
val chartMode = when (goal?.type) {
is RecordTypeGoal.Type.Duration -> ChartMode.DURATIONS
is RecordTypeGoal.Type.Count -> ChartMode.COUNTS
null -> ChartMode.DURATIONS
}
val ranges = chartInteractor.getRanges(
compositeData = compositeData,
rangeLength = rangeLength,
Expand All @@ -68,6 +81,7 @@ class StatisticsDetailGoalsInteractor @Inject constructor(
typesOrder = typesOrder,
typesMap = typesMap,
isDarkTheme = isDarkTheme,
chartMode = chartMode,
splitByActivity = false,
splitSortMode = ChartSplitSortMode.ACTIVITY_ORDER,
)
Expand All @@ -82,16 +96,11 @@ class StatisticsDetailGoalsInteractor @Inject constructor(
typesOrder = typesOrder,
typesMap = typesMap,
isDarkTheme = isDarkTheme,
chartMode = chartMode,
splitSortMode = ChartSplitSortMode.ACTIVITY_ORDER,
)
val goalValue = chartInteractor.getGoalValue(
goals = goals,
appliedChartGrouping = compositeData.appliedChartGrouping,
)
val goalSubtype = chartInteractor.getGoalSubtype(
goals = goals,
appliedChartGrouping = compositeData.appliedChartGrouping,
)
val goalValue = getGoalValue(goal)
val goalSubtype = goal?.subtype ?: RecordTypeGoal.Subtype.Goal

return@withContext StatisticsDetailGoalsCompositeViewData(
viewData = statisticsDetailViewDataMapper.mapGoalChartViewData(
Expand All @@ -113,6 +122,7 @@ class StatisticsDetailGoalsInteractor @Inject constructor(
appliedChartGrouping = compositeData.appliedChartGrouping,
availableChartLengths = compositeData.availableChartLengths,
appliedChartLength = compositeData.appliedChartLength,
chartMode = chartMode,
useProportionalMinutes = useProportionalMinutes,
showSeconds = showSeconds,
isDarkTheme = isDarkTheme,
Expand All @@ -137,7 +147,7 @@ class StatisticsDetailGoalsInteractor @Inject constructor(
)

val availableChartGroupings = mainData.availableChartGroupings
.filter { chartInteractor.getGoalValue(goals, it) != 0L }
.filter { getGoal(goals, it).value != 0L }
.takeUnless { it.isEmpty() }
?: listOf(ChartGrouping.DAILY)

Expand All @@ -149,4 +159,26 @@ class StatisticsDetailGoalsInteractor @Inject constructor(
?: ChartGrouping.DAILY,
)
}

private fun getGoal(
goals: List<RecordTypeGoal>,
appliedChartGrouping: ChartGrouping,
): RecordTypeGoal? {
return when (appliedChartGrouping) {
ChartGrouping.DAILY -> goals.getDaily()
ChartGrouping.WEEKLY -> goals.getWeekly()
ChartGrouping.MONTHLY -> goals.getMonthly()
ChartGrouping.YEARLY -> null
}
}

private fun getGoalValue(
goal: RecordTypeGoal?,
): Long {
return when (goal?.type) {
is RecordTypeGoal.Type.Duration -> goal.value * 1000
is RecordTypeGoal.Type.Count -> goal.value
null -> 0L
}
}
}
Loading

0 comments on commit deed522

Please sign in to comment.