diff --git a/home/src/main/java/com/jacob/wakatimeapp/home/domain/models/HomePageUiData.kt b/home/src/main/java/com/jacob/wakatimeapp/home/domain/models/HomePageUiData.kt index 4f93c0a9..51f3c6f0 100644 --- a/home/src/main/java/com/jacob/wakatimeapp/home/domain/models/HomePageUiData.kt +++ b/home/src/main/java/com/jacob/wakatimeapp/home/domain/models/HomePageUiData.kt @@ -4,12 +4,16 @@ import com.jacob.wakatimeapp.core.models.Project import com.jacob.wakatimeapp.core.models.Time import com.jacob.wakatimeapp.core.models.UserDetails import com.jacob.wakatimeapp.core.models.WeeklyStats +import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.Instant import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone import kotlinx.datetime.daysUntil +import kotlinx.datetime.minus +import kotlinx.datetime.plus import kotlinx.datetime.toLocalDateTime import kotlinx.serialization.Serializable +import timber.log.Timber @Serializable data class Last7DaysStats( @@ -40,6 +44,15 @@ data class StreakRange( ) { val days = start.daysUntil(end) + operator fun plus(other: StreakRange) = when { + end == other.start.minus(1, DateTimeUnit.DAY) -> StreakRange(start, other.end) + start == other.end.plus(1, DateTimeUnit.DAY) -> StreakRange(other.start, end) + else -> { + Timber.e("Cannot add streaks $this and $other") + ZERO + } + } + companion object { val ZERO = StreakRange( Instant.DISTANT_PAST.toLocalDateTime(TimeZone.currentSystemDefault()).date, diff --git a/home/src/main/java/com/jacob/wakatimeapp/home/domain/usecases/CalculateCurrentStreakUC.kt b/home/src/main/java/com/jacob/wakatimeapp/home/domain/usecases/CalculateCurrentStreakUC.kt index a4aef01d..c5afe226 100644 --- a/home/src/main/java/com/jacob/wakatimeapp/home/domain/usecases/CalculateCurrentStreakUC.kt +++ b/home/src/main/java/com/jacob/wakatimeapp/home/domain/usecases/CalculateCurrentStreakUC.kt @@ -2,6 +2,7 @@ package com.jacob.wakatimeapp.home.domain.usecases import arrow.core.Either import arrow.core.computations.either +import arrow.core.getOrElse import com.jacob.wakatimeapp.core.common.toDate import com.jacob.wakatimeapp.core.models.Error import com.jacob.wakatimeapp.core.models.Time @@ -14,6 +15,8 @@ import javax.inject.Inject import javax.inject.Singleton import kotlinx.coroutines.flow.first import kotlinx.datetime.DateTimeUnit +import kotlinx.datetime.LocalDate +import kotlinx.datetime.daysUntil import kotlinx.datetime.minus @Singleton @@ -62,15 +65,16 @@ class CalculateCurrentStreakUC @Inject constructor( last7DaysStats: Last7DaysStats, ): StreakRange { val statsForCurrentStreakRange = last7DaysStats.weeklyTimeSpent - .toSortedMap() - .entries - .reversed() - .takeWhile { it.value != Time.ZERO } + .getStreakInRange() if (statsForCurrentStreakRange.isEmpty()) return StreakRange.ZERO if (statsForCurrentStreakRange.size == 7) { - calculateStreak() + val start = instantProvider.now().toDate().minus(7, DateTimeUnit.DAY) + calculateStreak( + start, + start.minus(1, DateTimeUnit.MONTH) + ) } val start = if (currentStreak.end > statsForCurrentStreakRange.last().key) { @@ -84,4 +88,31 @@ class CalculateCurrentStreakUC @Inject constructor( end = statsForCurrentStreakRange.first().key, ) } + + private suspend fun calculateStreak(start: LocalDate, end: LocalDate): StreakRange { + val daysInRange = start.daysUntil(end) + return homePageNetworkData.getStatsForRange(start.toString(), end.toString()) + .map { stats -> + stats.dailyStats + .associate { it.date to it.timeSpent } + .getStreakInRange() + } + .map { StreakRange(start = it.last().key, end = it.first().key) } + .map { + if (it.days == daysInRange) { + it + calculateStreak( + start.minus(daysInRange, DateTimeUnit.DAY), + end.minus(daysInRange, DateTimeUnit.DAY) + ) + } else { + it + } + } + .getOrElse { StreakRange.ZERO } + } + + private fun Map.getStreakInRange() = toSortedMap() + .entries + .reversed() + .takeWhile { it.value != Time.ZERO } } diff --git a/home/src/test/java/com/jacob/wakatimeapp/home/domain/usecases/CalculateCurrentStreakUCRobot.kt b/home/src/test/java/com/jacob/wakatimeapp/home/domain/usecases/CalculateCurrentStreakUCRobot.kt index c466fd2d..c7b47bef 100644 --- a/home/src/test/java/com/jacob/wakatimeapp/home/domain/usecases/CalculateCurrentStreakUCRobot.kt +++ b/home/src/test/java/com/jacob/wakatimeapp/home/domain/usecases/CalculateCurrentStreakUCRobot.kt @@ -1,9 +1,13 @@ package com.jacob.wakatimeapp.home.domain.usecases import arrow.core.Either +import com.jacob.wakatimeapp.core.models.DailyStats import com.jacob.wakatimeapp.core.models.Error +import com.jacob.wakatimeapp.core.models.Stats +import com.jacob.wakatimeapp.core.models.StatsRange import com.jacob.wakatimeapp.core.models.Time import com.jacob.wakatimeapp.home.data.local.HomePageCache +import com.jacob.wakatimeapp.home.data.network.HomePageNetworkData import com.jacob.wakatimeapp.home.domain.InstantProvider import com.jacob.wakatimeapp.home.domain.models.Last7DaysStats import com.jacob.wakatimeapp.home.domain.models.StreakRange @@ -28,9 +32,10 @@ internal class CalculateCurrentStreakUCRobot { private var result: Either? = null private val mockCache: HomePageCache = mockk(relaxUnitFun = true) + private val mockNetworkData: HomePageNetworkData = mockk() fun buildUseCase() = apply { - clearMocks(mockCache) + clearMocks(mockCache, mockNetworkData) result = null useCase = CalculateCurrentStreakUC( @@ -39,7 +44,8 @@ internal class CalculateCurrentStreakUCRobot { override val timeZone = TimeZone.UTC override fun now() = currentDayInstant - } + }, + homePageNetworkData = mockNetworkData, ) } @@ -110,5 +116,36 @@ internal class CalculateCurrentStreakUCRobot { mostUsedEditor = "", mostUsedOs = "" ) + + val statsForRange = Stats( + totalTime = Time.ZERO, + dailyStats = listOf( + DailyStats( + Time.ZERO, + projectsWorkedOn = listOf(), + mostUsedLanguage = "", + mostUsedEditor = "", + mostUsedOs = "", + date = currentDay + ) + ), + range = StatsRange( + startDate = currentDay, + endDate = currentDay, + ) + + ) + + private fun createStatsForRange() { + Stats( + totalTime = Time.ZERO, + dailyStats = listOf(), + range = StatsRange( + startDate = currentDay, + endDate = currentDay, + ) + + ) + } } }