diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt index 5212923b1..e3596eecb 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/EntryList.kt @@ -276,6 +276,8 @@ open class EntryList { * For numerical habits, non-positive entry values are converted to zero. For boolean habits, each * YES_MANUAL value is converted to 1000 and all other values are converted to zero. * + * SKIP values are treated converted to zero (if they weren't, each SKIP day would count as 0.003). + * * The returned list is sorted by timestamp, with the newest entry coming first and the oldest entry * coming last. If the original list has gaps in it (for example, weeks or months without any * entries), then the list produced by this method will also have gaps. @@ -289,7 +291,10 @@ fun List.groupedSum( ): List { return this.map { (timestamp, value) -> if (isNumerical) { - Entry(timestamp, max(0, value)) + if(value == SKIP) + Entry(timestamp, 0) + else + Entry(timestamp, max(0, value)) } else { Entry(timestamp, if (value == YES_MANUAL) 1000 else 0) } @@ -304,3 +309,29 @@ fun List.groupedSum( - timestamp.unixTime } } + +/** + * Counts the number of days with vaLue SKIP in the given perio + */ +fun List.countSkippedDays( + truncateField: DateUtils.TruncateField, + firstWeekday: Int = Calendar.SATURDAY, + isNumerical: Boolean, +): List { + return this.map { (timestamp, value) -> + if (value == SKIP) { + Entry(timestamp, 1) + } else { + Entry(timestamp, 0) + } + }.groupBy { entry -> + entry.timestamp.truncate( + truncateField, + firstWeekday, + ) + }.entries.map { (timestamp, entries) -> + Entry(timestamp, entries.sumOf { it.value }) + }.sortedBy { (timestamp, _) -> + - timestamp.unixTime + } +} diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt index 15d500def..0c53a290d 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/show/views/TargetCard.kt @@ -21,6 +21,7 @@ package org.isoron.uhabits.core.ui.screens.habits.show.views import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.PaletteColor +import org.isoron.uhabits.core.models.countSkippedDays import org.isoron.uhabits.core.models.groupedSum import org.isoron.uhabits.core.ui.views.Theme import org.isoron.uhabits.core.utils.DateUtils @@ -51,37 +52,64 @@ class TargetCardPresenter { isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 + val skippedDayToday = entries.countSkippedDays( + truncateField = DateUtils.TruncateField.DAY, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + val valueThisWeek = entries.groupedSum( truncateField = DateUtils.TruncateField.WEEK_NUMBER, firstWeekday = firstWeekday, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 + val skippedDaysThisWeek = entries.countSkippedDays( + truncateField = DateUtils.TruncateField.WEEK_NUMBER, + firstWeekday = firstWeekday, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + val valueThisMonth = entries.groupedSum( truncateField = DateUtils.TruncateField.MONTH, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 + val skippedDaysThisMonth = entries.countSkippedDays( + truncateField = DateUtils.TruncateField.MONTH, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + val valueThisQuarter = entries.groupedSum( truncateField = DateUtils.TruncateField.QUARTER, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 + val skippedDaysThisQuarter = entries.countSkippedDays( + truncateField = DateUtils.TruncateField.QUARTER, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + val valueThisYear = entries.groupedSum( truncateField = DateUtils.TruncateField.YEAR, isNumerical = habit.isNumerical ).firstOrNull()?.value ?: 0 + val skippedDaysThisYear = entries.countSkippedDays( + truncateField = DateUtils.TruncateField.YEAR, + isNumerical = habit.isNumerical + ).firstOrNull()?.value ?: 0 + val cal = DateUtils.getStartOfTodayCalendarWithOffset() val daysInMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH) val daysInQuarter = 91 val daysInYear = cal.getActualMaximum(Calendar.DAY_OF_YEAR) - val targetToday = habit.targetValue / habit.frequency.denominator - val targetThisWeek = targetToday * 7 - val targetThisMonth = targetToday * daysInMonth - val targetThisQuarter = targetToday * daysInQuarter - val targetThisYear = targetToday * daysInYear + val dailyTarget = habit.targetValue / habit.frequency.denominator + val targetToday = dailyTarget * (1 - skippedDayToday) + val targetThisWeek = dailyTarget * (7 - skippedDaysThisWeek) + val targetThisMonth = dailyTarget * (daysInMonth - skippedDaysThisMonth) + val targetThisQuarter = dailyTarget * (daysInQuarter - skippedDaysThisQuarter) + val targetThisYear = dailyTarget * (daysInYear - skippedDaysThisYear) val values = ArrayList() if (habit.frequency.denominator <= 1) values.add(valueToday / 1e3)