mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 01:08:50 -06:00
Implement numerical habits with AT_MOST target type
This commit is contained in:
@@ -59,9 +59,10 @@ data class Habit(
|
||||
val today = DateUtils.getTodayWithOffset()
|
||||
val value = computedEntries.get(today).value
|
||||
return if (isNumerical) {
|
||||
val targetValuePerDay = (targetValue / frequency.denominator)
|
||||
when (targetType) {
|
||||
NumericalHabitType.AT_LEAST -> value / 1000.0 >= targetValue
|
||||
NumericalHabitType.AT_MOST -> value / 1000.0 <= targetValue
|
||||
NumericalHabitType.AT_LEAST -> value / 1000.0 >= targetValuePerDay
|
||||
NumericalHabitType.AT_MOST -> value / 1000.0 <= targetValuePerDay
|
||||
}
|
||||
} else {
|
||||
value != Entry.NO && value != Entry.UNKNOWN
|
||||
@@ -72,9 +73,10 @@ data class Habit(
|
||||
val today = DateUtils.getTodayWithOffset()
|
||||
val value = computedEntries.get(today).value
|
||||
return if (isNumerical) {
|
||||
val targetValuePerDay = (targetValue / frequency.denominator)
|
||||
when (targetType) {
|
||||
NumericalHabitType.AT_LEAST -> value / 1000.0 < targetValue
|
||||
NumericalHabitType.AT_MOST -> value / 1000.0 > targetValue
|
||||
NumericalHabitType.AT_LEAST -> value / 1000.0 < targetValuePerDay
|
||||
NumericalHabitType.AT_MOST -> value / 1000.0 > targetValuePerDay
|
||||
}
|
||||
} else {
|
||||
value == Entry.NO
|
||||
@@ -96,6 +98,7 @@ data class Habit(
|
||||
scores.recompute(
|
||||
frequency = frequency,
|
||||
isNumerical = isNumerical,
|
||||
numericalHabitType = targetType,
|
||||
targetValue = targetValue,
|
||||
computedEntries = computedEntries,
|
||||
from = from,
|
||||
|
||||
@@ -68,6 +68,7 @@ class ScoreList {
|
||||
fun recompute(
|
||||
frequency: Frequency,
|
||||
isNumerical: Boolean,
|
||||
numericalHabitType: NumericalHabitType,
|
||||
targetValue: Double,
|
||||
computedEntries: EntryList,
|
||||
from: Timestamp,
|
||||
@@ -91,18 +92,37 @@ class ScoreList {
|
||||
}
|
||||
|
||||
var previousValue = 0.0
|
||||
val numericalUnknownDayValue = (targetValue * 2 * 1000) / denominator
|
||||
for (i in values.indices) {
|
||||
val offset = values.size - i - 1
|
||||
if (isNumerical) {
|
||||
rollingSum += max(0, values[offset])
|
||||
if (values[offset] >= 0)
|
||||
rollingSum += values[offset]
|
||||
else if (numericalHabitType == NumericalHabitType.AT_MOST)
|
||||
rollingSum += numericalUnknownDayValue
|
||||
if (offset + denominator < values.size) {
|
||||
rollingSum -= values[offset + denominator]
|
||||
if (values[offset + denominator] >= 0) {
|
||||
rollingSum -= values[offset + denominator]
|
||||
} else if (numericalHabitType == NumericalHabitType.AT_MOST) {
|
||||
rollingSum -= numericalUnknownDayValue
|
||||
}
|
||||
}
|
||||
val percentageCompleted = if (targetValue > 0) {
|
||||
min(1.0, rollingSum / 1000 / targetValue)
|
||||
} else {
|
||||
1.0
|
||||
|
||||
var percentageCompleted = 0.0
|
||||
val normalizedRollingSum = rollingSum / 1000
|
||||
if (numericalHabitType == NumericalHabitType.AT_LEAST) {
|
||||
percentageCompleted = if (targetValue > 0)
|
||||
min(1.0, normalizedRollingSum / targetValue)
|
||||
else
|
||||
1.0
|
||||
} else if (numericalHabitType == NumericalHabitType.AT_MOST) {
|
||||
percentageCompleted = if (targetValue > 0 && normalizedRollingSum > targetValue)
|
||||
max(
|
||||
0.0, 1 - ((normalizedRollingSum - targetValue) / targetValue)
|
||||
)
|
||||
else if (normalizedRollingSum <= targetValue) 1.0 else 0.0
|
||||
}
|
||||
|
||||
previousValue = compute(freq, previousValue, percentageCompleted)
|
||||
} else {
|
||||
if (values[offset] == Entry.YES_MANUAL) {
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO
|
||||
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
||||
import org.isoron.uhabits.core.models.Habit
|
||||
import org.isoron.uhabits.core.models.HabitList
|
||||
import org.isoron.uhabits.core.models.NumericalHabitType
|
||||
import org.isoron.uhabits.core.models.PaletteColor
|
||||
import org.isoron.uhabits.core.models.Timestamp
|
||||
import org.isoron.uhabits.core.preferences.Preferences
|
||||
@@ -105,12 +106,21 @@ class HistoryCardPresenter(
|
||||
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
|
||||
val entries = habit.computedEntries.getByInterval(oldest, today)
|
||||
val series = if (habit.isNumerical) {
|
||||
entries.map {
|
||||
Entry(it.timestamp, max(0, it.value))
|
||||
}.map {
|
||||
when (it.value) {
|
||||
0 -> HistoryChart.Square.OFF
|
||||
else -> HistoryChart.Square.ON
|
||||
if (habit.targetType == NumericalHabitType.AT_LEAST) {
|
||||
entries.map {
|
||||
when (max(0, it.value)) {
|
||||
0 -> HistoryChart.Square.OFF
|
||||
else -> HistoryChart.Square.ON
|
||||
}
|
||||
}
|
||||
} else {
|
||||
entries.map {
|
||||
if (it.value < 0) habit.targetValue * 2.0 * 1000.0 else it.value / 1000.0
|
||||
}.map {
|
||||
when {
|
||||
it <= habit.targetValue -> HistoryChart.Square.ON
|
||||
else -> HistoryChart.Square.OFF
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -128,6 +128,10 @@ class ScoreListTest : BaseUnitTest() {
|
||||
habit.targetValue = 0.0
|
||||
habit.recompute()
|
||||
assertTrue(habit.scores[today].value.isFinite())
|
||||
|
||||
habit.targetType = NumericalHabitType.AT_MOST
|
||||
habit.recompute()
|
||||
assertTrue(habit.scores[today].value.isFinite())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user