Skip to content

Commit

Permalink
account for goal days in statistics detail
Browse files Browse the repository at this point in the history
  • Loading branch information
Razeeman committed Jan 25, 2025
1 parent 6943bea commit 4ccc935
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 19 deletions.
1 change: 0 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import com.example.util.simpletimetracker.Base
import com.example.util.simpletimetracker.applyAndroidLibrary
import dagger.hilt.android.plugin.util.capitalize

plugins {
alias(libs.plugins.gradleApplication)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.util.simpletimetracker.core.extension.setToStartOfDay
import com.example.util.simpletimetracker.utils.BaseUiTest
import com.example.util.simpletimetracker.utils.NavUtils
import com.example.util.simpletimetracker.utils.checkViewDoesNotExist
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.example.util.simpletimetracker.feature_base_adapter.runningRecord

import androidx.core.view.ViewCompat
import com.example.util.simpletimetracker.domain.extension.orFalse
import com.example.util.simpletimetracker.domain.record.interactor.UpdateRunningRecordFromChangeScreenInteractor.GoalState
import com.example.util.simpletimetracker.feature_base_adapter.createRecyclerBindingAdapterDelegate
import com.example.util.simpletimetracker.feature_base_adapter.runningRecord.GoalTimeViewData.Subtype
import com.example.util.simpletimetracker.feature_views.GoalCheckmarkView.CheckState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class RecordQuickActionsInteractor @Inject constructor(
newTagIds = getTagsAfterActivityChange(
currentTags = record.tagIds,
newTypeId = newTypeId,
)
),
)
}
is Type.RecordUntracked -> {
Expand All @@ -64,7 +64,7 @@ class RecordQuickActionsInteractor @Inject constructor(
newTagIds = getTagsAfterActivityChange(
currentTags = record.tagIds,
newTypeId = newTypeId,
)
),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,13 @@ class StatisticsDetailChartInteractor @Inject constructor(
splitSortMode: ChartSplitSortMode,
): List<ChartBarDataDuration> {
fun mapEmpty(): List<ChartBarDataDuration> {
return ranges.map { ChartBarDataDuration(legend = it.legend, durations = listOf(0L to 0)) }
return ranges.map {
ChartBarDataDuration(
rangeStart = it.rangeStart,
legend = it.legend,
durations = listOf(0L to 0),
)
}
}

fun mapRangesToValue(list: List<Range>): Long {
Expand Down Expand Up @@ -244,6 +250,7 @@ class StatisticsDetailChartInteractor @Inject constructor(
}

ChartBarDataDuration(
rangeStart = data.rangeStart,
legend = data.legend,
durations = durations,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ class StatisticsDetailDataDistributionInteractor @Inject constructor(
return when {
statistics.id == UNTRACKED_ITEM_ID -> {
ChartBarDataDuration(
rangeStart = 0, // Not needed.
legend = "",
durations = listOf(
statistics.data.duration to
Expand All @@ -244,6 +245,7 @@ class StatisticsDetailDataDistributionInteractor @Inject constructor(
}
statistics.id == UNCATEGORIZED_ITEM_ID -> {
ChartBarDataDuration(
rangeStart = 0,
legend = "",
durations = listOf(
statistics.data.duration to
Expand All @@ -253,6 +255,7 @@ class StatisticsDetailDataDistributionInteractor @Inject constructor(
}
dataHolder != null -> {
ChartBarDataDuration(
rangeStart = 0,
legend = "",
durations = listOf(
statistics.data.duration to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class StatisticsDetailGoalsInteractor @Inject constructor(
useProportionalMinutes = useProportionalMinutes,
showSeconds = showSeconds,
isDarkTheme = isDarkTheme,
startOfDayShift = startOfDayShift,
)

return@withContext StatisticsDetailGoalsCompositeViewData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import com.example.util.simpletimetracker.feature_statistics_detail.viewData.Sta
import com.example.util.simpletimetracker.feature_statistics_detail.viewData.StatisticsDetailPreviewViewData
import com.example.util.simpletimetracker.feature_statistics_detail.viewData.StatisticsDetailSplitGroupingViewData
import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon
import java.util.Calendar
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.math.abs
Expand Down Expand Up @@ -511,15 +512,15 @@ class StatisticsDetailViewDataMapper @Inject constructor(
}

val average = getAverage(data)
val nonEmptyData = data.filter { it.durations.sumOf { it.first } != 0L }
val nonEmptyData = data.filter { it.totalDuration != 0L }
val averageByNonEmpty = getAverage(nonEmptyData)

val comparisonAverage = getAverage(compareData)
val comparisonNonEmptyData = compareData.filter { it.durations.sumOf { it.first } != 0L }
val comparisonNonEmptyData = compareData.filter { it.totalDuration != 0L }
val comparisonAverageByNonEmpty = getAverage(comparisonNonEmptyData)

val prevAverage = getAverage(prevData)
val prevNonEmptyData = prevData.filter { it.durations.sumOf { it.first } != 0L }
val prevNonEmptyData = prevData.filter { it.totalDuration != 0L }
val prevAverageByNonEmpty = getAverage(prevNonEmptyData)

val title = resourceRepo.getString(
Expand Down Expand Up @@ -682,13 +683,17 @@ class StatisticsDetailViewDataMapper @Inject constructor(
)
}

fun mapGoalData(
private fun mapGoalData(
data: List<ChartBarDataDuration>,
goalValue: Long,
goalSubtype: RecordTypeGoal.Subtype,
goalRange: RecordTypeGoal.Range,
goalDaysOfWeek: Set<DayOfWeek>,
isDarkTheme: Boolean,
startOfDayShift: Long,
): List<ChartBarDataDuration> {
if (goalValue == 0L) return emptyList()
val shouldAccountForDays = goalRange is RecordTypeGoal.Range.Daily
val greenColor = resourceRepo.getThemedAttr(R.attr.appPositiveColor, isDarkTheme)
val redColor = resourceRepo.getThemedAttr(R.attr.appNegativeColor, isDarkTheme)
val positiveColor = when (goalSubtype) {
Expand All @@ -699,14 +704,30 @@ class StatisticsDetailViewDataMapper @Inject constructor(
is RecordTypeGoal.Subtype.Goal -> redColor
is RecordTypeGoal.Subtype.Limit -> greenColor
}
val calendar = Calendar.getInstance()
val current = System.currentTimeMillis()

return data.map { dataPart ->
val totalDuration = dataPart.durations.sumOf { it.first }
// Show difference from goal value only on days
// when there were records tracked.
val goalDuration = if (totalDuration != 0L) totalDuration - goalValue else 0L
val totalDuration = dataPart.totalDuration
val goalDuration = if (dataPart.rangeStart > current) {
0
} else if (shouldAccountForDays) {
val currentPartDay = timeMapper.getDayOfWeek(
timestamp = dataPart.rangeStart,
calendar = calendar,
startOfDayShift = startOfDayShift,
)
if (currentPartDay in goalDaysOfWeek) {
totalDuration - goalValue
} else {
0
}
} else {
totalDuration - goalValue
}
val color = if (goalDuration >= 0) positiveColor else negativeColor
ChartBarDataDuration(
rangeStart = dataPart.rangeStart,
legend = dataPart.legend,
durations = listOf(goalDuration to color),
)
Expand Down Expand Up @@ -772,23 +793,32 @@ class StatisticsDetailViewDataMapper @Inject constructor(
useProportionalMinutes: Boolean,
showSeconds: Boolean,
isDarkTheme: Boolean,
startOfDayShift: Long,
): List<ViewHolderType> {
val goalValue = getGoalValue(chartGoal)
if (goalValue == 0L) return emptyList()
val goalRange = chartGoal?.range ?: return emptyList()
val goalSubtype = chartGoal.subtype
val goalDaysOfWeek = chartGoal.daysOfWeek

val items = mutableListOf<ViewHolderType>()
val goalSubtype = chartGoal?.subtype ?: RecordTypeGoal.Subtype.Goal
val goalData = mapGoalData(
data = data,
goalValue = goalValue,
goalRange = goalRange,
goalDaysOfWeek = goalDaysOfWeek,
goalSubtype = goalSubtype,
isDarkTheme = isDarkTheme,
startOfDayShift = startOfDayShift,
)
val goalChartPrevData = mapGoalData(
data = prevData,
goalValue = goalValue,
goalRange = goalRange,
goalDaysOfWeek = goalDaysOfWeek,
goalSubtype = goalSubtype,
isDarkTheme = isDarkTheme,
startOfDayShift = startOfDayShift,
)
val chartData = mapChartData(
data = goalData,
Expand Down Expand Up @@ -886,7 +916,7 @@ class StatisticsDetailViewDataMapper @Inject constructor(
data: List<ChartBarDataDuration>,
): Pair<String, Boolean> {
val isMinutes = data
.maxOfOrNull { barPart -> abs(barPart.durations.sumOf { it.first }) }
.maxOfOrNull { barPart -> abs(barPart.totalDuration) }
.orZero()
.let(TimeUnit.MILLISECONDS::toHours) == 0L

Expand Down Expand Up @@ -976,7 +1006,7 @@ class StatisticsDetailViewDataMapper @Inject constructor(
useProportionalMinutes: Boolean,
showSeconds: Boolean,
): List<StatisticsDetailCardInternalViewData> {
val barValues = goalData.map { bar -> bar.durations.sumOf { it.first } }
val barValues = goalData.map { bar -> bar.totalDuration }
val negativeValue = barValues.filter { it < 0L }.sum()
val positiveValue = barValues.filter { it > 0L }.sum()
val total = negativeValue + positiveValue
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.example.util.simpletimetracker.feature_statistics_detail.model

class ChartBarDataDuration(
data class ChartBarDataDuration(
val rangeStart: Long,
val legend: String,
val durations: List<Pair<Long, Int>>,
)
) {

val totalDuration = durations.sumOf { it.first }
}

0 comments on commit 4ccc935

Please sign in to comment.