From 268d66e51d96f9e77069ac6f00bde5c16fe5e706 Mon Sep 17 00:00:00 2001 From: Nishant Mishra Date: Sat, 12 Jul 2025 18:45:24 +0530 Subject: [PATCH] refactor(internal): Add TypeConvertor for LocalDate to directly handle LocalDate in database --- .../org/nsh07/pomodoro/data/AppDatabase.kt | 2 + .../org/nsh07/pomodoro/data/Converters.kt | 23 ++ .../main/java/org/nsh07/pomodoro/data/Stat.kt | 5 +- .../java/org/nsh07/pomodoro/data/StatDao.kt | 17 +- .../org/nsh07/pomodoro/data/StatRepository.kt | 10 +- .../java/org/nsh07/pomodoro/ui/AppScreen.kt | 8 +- .../ui/statsScreen/ProductivityGraph.kt | 52 ++++ .../pomodoro/ui/statsScreen/StatsScreen.kt | 255 +++++++++--------- .../ui/statsScreen/TimeColumnChart.kt | 15 +- .../statsScreen/viewModel/StatsViewModel.kt | 9 +- .../timerScreen/viewModel/TimerViewModel.kt | 10 +- 11 files changed, 250 insertions(+), 156 deletions(-) create mode 100644 app/src/main/java/org/nsh07/pomodoro/data/Converters.kt create mode 100644 app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/ProductivityGraph.kt diff --git a/app/src/main/java/org/nsh07/pomodoro/data/AppDatabase.kt b/app/src/main/java/org/nsh07/pomodoro/data/AppDatabase.kt index ea86a1f..8e457cf 100644 --- a/app/src/main/java/org/nsh07/pomodoro/data/AppDatabase.kt +++ b/app/src/main/java/org/nsh07/pomodoro/data/AppDatabase.kt @@ -11,11 +11,13 @@ import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase +import androidx.room.TypeConverters @Database( entities = [IntPreference::class, Stat::class], version = 1 ) +@TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun preferenceDao(): PreferenceDao diff --git a/app/src/main/java/org/nsh07/pomodoro/data/Converters.kt b/app/src/main/java/org/nsh07/pomodoro/data/Converters.kt new file mode 100644 index 0000000..431249d --- /dev/null +++ b/app/src/main/java/org/nsh07/pomodoro/data/Converters.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 Nishant Mishra + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.nsh07.pomodoro.data + +import androidx.room.TypeConverter +import java.time.LocalDate + +class Converters { + @TypeConverter + fun localDateToString(localDate: LocalDate?): String? { + return localDate?.toString() + } + + @TypeConverter + fun stringToLocalDate(date: String?): LocalDate? { + return if (date != null) LocalDate.parse(date) else null + } +} \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/data/Stat.kt b/app/src/main/java/org/nsh07/pomodoro/data/Stat.kt index 80a683f..2ad928e 100644 --- a/app/src/main/java/org/nsh07/pomodoro/data/Stat.kt +++ b/app/src/main/java/org/nsh07/pomodoro/data/Stat.kt @@ -9,6 +9,7 @@ package org.nsh07.pomodoro.data import androidx.room.Entity import androidx.room.PrimaryKey +import java.time.LocalDate /** * Data class for storing the user's statistics in the app's database. This class stores the focus @@ -18,7 +19,7 @@ import androidx.room.PrimaryKey @Entity(tableName = "stat") data class Stat( @PrimaryKey - val date: String, + val date: LocalDate, val focusTimeQ1: Long, val focusTimeQ2: Long, val focusTimeQ3: Long, @@ -27,7 +28,7 @@ data class Stat( ) data class StatSummary( - val date: String, + val date: LocalDate, val focusTime: Long, val breakTime: Long ) diff --git a/app/src/main/java/org/nsh07/pomodoro/data/StatDao.kt b/app/src/main/java/org/nsh07/pomodoro/data/StatDao.kt index cd448a5..d18026b 100644 --- a/app/src/main/java/org/nsh07/pomodoro/data/StatDao.kt +++ b/app/src/main/java/org/nsh07/pomodoro/data/StatDao.kt @@ -12,6 +12,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy.Companion.REPLACE import androidx.room.Query import kotlinx.coroutines.flow.Flow +import java.time.LocalDate @Dao interface StatDao { @@ -19,22 +20,22 @@ interface StatDao { suspend fun insertStat(stat: Stat) @Query("UPDATE stat SET focusTimeQ1 = focusTimeQ1 + :focusTime WHERE date = :date") - suspend fun addFocusTimeQ1(date: String, focusTime: Long) + suspend fun addFocusTimeQ1(date: LocalDate, focusTime: Long) @Query("UPDATE stat SET focusTimeQ2 = focusTimeQ2 + :focusTime WHERE date = :date") - suspend fun addFocusTimeQ2(date: String, focusTime: Long) + suspend fun addFocusTimeQ2(date: LocalDate, focusTime: Long) @Query("UPDATE stat SET focusTimeQ3 = focusTimeQ3 + :focusTime WHERE date = :date") - suspend fun addFocusTimeQ3(date: String, focusTime: Long) + suspend fun addFocusTimeQ3(date: LocalDate, focusTime: Long) @Query("UPDATE stat SET focusTimeQ4 = focusTimeQ4 + :focusTime WHERE date = :date") - suspend fun addFocusTimeQ4(date: String, focusTime: Long) + suspend fun addFocusTimeQ4(date: LocalDate, focusTime: Long) @Query("UPDATE stat SET breakTime = breakTime + :breakTime WHERE date = :date") - suspend fun addBreakTime(date: String, breakTime: Long) + suspend fun addBreakTime(date: LocalDate, breakTime: Long) @Query("SELECT * FROM stat WHERE date = :date") - fun getStat(date: String): Flow + fun getStat(date: LocalDate): Flow @Query("SELECT date, focusTimeQ1 + focusTimeQ2 + focusTimeQ3 + focusTimeQ4 as focusTime, breakTime FROM stat ORDER BY date DESC LIMIT 7") fun getLastWeekStatsSummary(): Flow> @@ -43,8 +44,8 @@ interface StatDao { fun getAvgFocusTimes(): Flow @Query("SELECT EXISTS (SELECT * FROM stat WHERE date = :date)") - suspend fun statExists(date: String): Boolean + suspend fun statExists(date: LocalDate): Boolean @Query("SELECT date FROM stat ORDER BY date DESC LIMIT 1") - suspend fun getLastDate(): String? + suspend fun getLastDate(): LocalDate? } \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/data/StatRepository.kt b/app/src/main/java/org/nsh07/pomodoro/data/StatRepository.kt index f9b41ce..be027ba 100644 --- a/app/src/main/java/org/nsh07/pomodoro/data/StatRepository.kt +++ b/app/src/main/java/org/nsh07/pomodoro/data/StatRepository.kt @@ -32,7 +32,7 @@ interface StatRepository { fun getAverageFocusTimes(): Flow - suspend fun getLastDate(): String? + suspend fun getLastDate(): LocalDate? } /** @@ -45,7 +45,7 @@ class AppStatRepository( override suspend fun insertStat(stat: Stat) = statDao.insertStat(stat) override suspend fun addFocusTime(focusTime: Long) = withContext(ioDispatcher) { - val currentDate = LocalDate.now().toString() + val currentDate = LocalDate.now() val currentTime = LocalTime.now().toSecondOfDay() val secondsInDay = 24 * 60 * 60 @@ -88,7 +88,7 @@ class AppStatRepository( } override suspend fun addBreakTime(breakTime: Long) = withContext(ioDispatcher) { - val currentDate = LocalDate.now().toString() + val currentDate = LocalDate.now() if (statDao.statExists(currentDate)) { statDao.addBreakTime(currentDate, breakTime) } else { @@ -97,7 +97,7 @@ class AppStatRepository( } override fun getTodayStat(): Flow { - val currentDate = LocalDate.now().toString() + val currentDate = LocalDate.now() return statDao.getStat(currentDate) } @@ -106,5 +106,5 @@ class AppStatRepository( override fun getAverageFocusTimes(): Flow = statDao.getAvgFocusTimes() - override suspend fun getLastDate(): String? = statDao.getLastDate() + override suspend fun getLastDate(): LocalDate? = statDao.getLastDate() } \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/AppScreen.kt b/app/src/main/java/org/nsh07/pomodoro/ui/AppScreen.kt index ce00f64..072cb66 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/AppScreen.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/AppScreen.kt @@ -157,7 +157,13 @@ fun AppScreen( } entry { - StatsScreenRoot() + StatsScreenRoot( + modifier = modifier.padding( + start = contentPadding.calculateStartPadding(layoutDirection), + end = contentPadding.calculateEndPadding(layoutDirection), + bottom = contentPadding.calculateBottomPadding() + ) + ) } } ) diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/ProductivityGraph.kt b/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/ProductivityGraph.kt new file mode 100644 index 0000000..ceb2fb1 --- /dev/null +++ b/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/ProductivityGraph.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2025 Nishant Mishra + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.nsh07.pomodoro.ui.statsScreen + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.material3.MaterialTheme.typography +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer +import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter +import org.nsh07.pomodoro.utils.millisecondsToHoursMinutes + +@Composable +fun ColumnScope.ProductivityGraph( + expanded: Boolean, + modelProducer: CartesianChartModelProducer, + modifier: Modifier = Modifier +) { + AnimatedVisibility(expanded) { + Column(modifier = modifier) { + Text("Productivity analysis", style = typography.titleMedium) + Text("Time of day versus focus hours", style = typography.bodySmall) + Spacer(Modifier.height(8.dp)) + TimeColumnChart( + modelProducer, + yValueFormatter = CartesianValueFormatter { _, value, _ -> + millisecondsToHoursMinutes(value.toLong()) + }, + xValueFormatter = CartesianValueFormatter { _, value, _ -> + when (value) { + 0.0 -> "0 - 6" + 1.0 -> "6 - 12" + 2.0 -> "12 - 18" + 3.0 -> "18 - 24" + else -> "" + } + } + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/StatsScreen.kt b/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/StatsScreen.kt index 7cd8f25..d7c2b6c 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/StatsScreen.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/StatsScreen.kt @@ -7,7 +7,6 @@ package org.nsh07.pomodoro.ui.statsScreen -import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -31,6 +30,7 @@ import androidx.compose.material3.MaterialTheme.motionScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -41,6 +41,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview @@ -64,7 +65,7 @@ fun StatsScreenRoot( ) { val todayStat by viewModel.todayStat.collectAsState(null) StatsScreen( - allStatsSummaryModelProducer = viewModel.allStatsSummaryModelProducer, + lastWeekSummaryModelProducer = viewModel.lastWeekSummaryChartModelProducer, todayStatModelProducer = viewModel.todayStatModelProducer, todayStat = todayStat, modifier = modifier @@ -74,150 +75,150 @@ fun StatsScreenRoot( @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable fun StatsScreen( - allStatsSummaryModelProducer: CartesianChartModelProducer, + lastWeekSummaryModelProducer: CartesianChartModelProducer, todayStatModelProducer: CartesianChartModelProducer, todayStat: Stat?, modifier: Modifier = Modifier ) { + val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() + var todayStatExpanded by rememberSaveable { mutableStateOf(false) } - LazyColumn( + Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier + modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection) ) { - item { - TopAppBar( - title = { - Text( - "Stats", - style = LocalTextStyle.current.copy( - fontFamily = robotoFlexTopBar, - fontSize = 32.sp, - lineHeight = 32.sp - ) + TopAppBar( + title = { + Text( + "Stats", + style = LocalTextStyle.current.copy( + fontFamily = robotoFlexTopBar, + fontSize = 32.sp, + lineHeight = 32.sp ) - }, - subtitle = {}, - titleHorizontalAlignment = Alignment.CenterHorizontally - ) - } - item { - Text( - "Today", - style = typography.headlineSmall, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp) - ) - Spacer(Modifier.height(16.dp)) - } - item { - Row(modifier = Modifier.padding(horizontal = 16.dp)) { - Box( + ) + }, + subtitle = {}, + titleHorizontalAlignment = Alignment.CenterHorizontally, + scrollBehavior = scrollBehavior + ) + + LazyColumn(horizontalAlignment = Alignment.CenterHorizontally) { + item { + Text( + "Today", + style = typography.headlineSmall, modifier = Modifier - .background( - colorScheme.primaryContainer, - MaterialTheme.shapes.largeIncreased - ) - .weight(1f) - ) { - Column(Modifier.padding(16.dp)) { - Text( - "Focus", - style = typography.titleMedium, - color = colorScheme.onPrimaryContainer - ) - Text( - if (todayStat != null) remember(todayStat) { - millisecondsToHoursMinutes( - todayStat.focusTimeQ1 + - todayStat.focusTimeQ2 + - todayStat.focusTimeQ3 + - todayStat.focusTimeQ4 - ) - } else "0h 0m", - style = typography.displaySmall, - fontFamily = openRundeClock, - color = colorScheme.onPrimaryContainer - ) + .fillMaxWidth() + .padding(16.dp) + ) + } + item { + Row(modifier = Modifier.padding(horizontal = 16.dp)) { + Box( + modifier = Modifier + .background( + colorScheme.primaryContainer, + MaterialTheme.shapes.largeIncreased + ) + .weight(1f) + ) { + Column(Modifier.padding(16.dp)) { + Text( + "Focus", + style = typography.titleMedium, + color = colorScheme.onPrimaryContainer + ) + Text( + if (todayStat != null) remember(todayStat) { + millisecondsToHoursMinutes( + todayStat.focusTimeQ1 + + todayStat.focusTimeQ2 + + todayStat.focusTimeQ3 + + todayStat.focusTimeQ4 + ) + } else "0h 0m", + style = typography.displaySmall, + fontFamily = openRundeClock, + color = colorScheme.onPrimaryContainer + ) + } } - } - Spacer(Modifier.width(8.dp)) - Box( - modifier = Modifier - .background( - colorScheme.tertiaryContainer, - MaterialTheme.shapes.largeIncreased - ) - .weight(1f) - ) { - Column(Modifier.padding(16.dp)) { - Text( - "Break", - style = typography.titleMedium, - color = colorScheme.onTertiaryContainer - ) - Text( - if (todayStat != null) remember(todayStat) { - millisecondsToHoursMinutes(todayStat.breakTime) - } else "0h 0m", - style = typography.displaySmall, - fontFamily = openRundeClock, - color = colorScheme.onTertiaryContainer - ) + Spacer(Modifier.width(8.dp)) + Box( + modifier = Modifier + .background( + colorScheme.tertiaryContainer, + MaterialTheme.shapes.largeIncreased + ) + .weight(1f) + ) { + Column(Modifier.padding(16.dp)) { + Text( + "Break", + style = typography.titleMedium, + color = colorScheme.onTertiaryContainer + ) + Text( + if (todayStat != null) remember(todayStat) { + millisecondsToHoursMinutes(todayStat.breakTime) + } else "0h 0m", + style = typography.displaySmall, + fontFamily = openRundeClock, + color = colorScheme.onTertiaryContainer + ) + } } } } - } - item { - val iconRotation by animateFloatAsState( - if (todayStatExpanded) 180f else 0f, - animationSpec = motionScheme.defaultSpatialSpec() - ) - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth() - ) { - FilledTonalIconToggleButton( - checked = todayStatExpanded, - onCheckedChange = { todayStatExpanded = it }, - shapes = IconButtonDefaults.toggleableShapes(), - modifier = Modifier - .padding(horizontal = 16.dp) - .width(52.dp) - .align(Alignment.End) + item { + val iconRotation by animateFloatAsState( + if (todayStatExpanded) 180f else 0f, + animationSpec = motionScheme.defaultSpatialSpec() + ) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth() ) { - Icon( - painterResource(R.drawable.arrow_down), - "More info", - modifier = Modifier.rotate(iconRotation) - ) - } - AnimatedVisibility(todayStatExpanded) { - TimeColumnChart( + FilledTonalIconToggleButton( + checked = todayStatExpanded, + onCheckedChange = { todayStatExpanded = it }, + shapes = IconButtonDefaults.toggleableShapes(), + modifier = Modifier + .padding(horizontal = 16.dp) + .width(52.dp) + .align(Alignment.End) + ) { + Icon( + painterResource(R.drawable.arrow_down), + "More info", + modifier = Modifier.rotate(iconRotation) + ) + } + ProductivityGraph( + todayStatExpanded, todayStatModelProducer, - timeConverter = ::millisecondsToHoursMinutes, - modifier = Modifier.padding(start = 16.dp) + Modifier.padding(horizontal = 32.dp) ) } + Spacer(Modifier.height(16.dp)) + } + item { + Text( + "This week", + style = typography.headlineSmall, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) + } + item { + TimeColumnChart( + lastWeekSummaryModelProducer, + modifier = Modifier.padding(start = 16.dp) + ) } - Spacer(Modifier.height(16.dp)) - } - item { - Text( - "This week", - style = typography.headlineSmall, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp) - ) - Spacer(Modifier.height(16.dp)) - } - item { - TimeColumnChart( - allStatsSummaryModelProducer, - modifier = Modifier.padding(start = 16.dp) - ) } } } diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/TimeColumnChart.kt b/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/TimeColumnChart.kt index 1925d4f..d468ea4 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/TimeColumnChart.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/TimeColumnChart.kt @@ -41,7 +41,11 @@ internal fun TimeColumnChart( modelProducer: CartesianChartModelProducer, modifier: Modifier = Modifier, thickness: Dp = 40.dp, - timeConverter: (Long) -> String = ::millisecondsToHours + columnCollectionSpacing: Dp = 4.dp, + xValueFormatter: CartesianValueFormatter = CartesianValueFormatter.Default, + yValueFormatter: CartesianValueFormatter = CartesianValueFormatter { measuringContext, value, _ -> + millisecondsToHours(value.toLong()) + } ) { val radius = with(LocalDensity.current) { (thickness / 2).toPx() @@ -82,20 +86,19 @@ internal fun TimeColumnChart( ) } ), - columnCollectionSpacing = 4.dp + columnCollectionSpacing = columnCollectionSpacing ), startAxis = VerticalAxis.rememberStart( line = rememberLineComponent(Fill.Transparent), tick = rememberLineComponent(Fill.Transparent), guideline = rememberLineComponent(Fill.Transparent), - valueFormatter = CartesianValueFormatter { measuringContext, value, _ -> - timeConverter(value.toLong()) - } + valueFormatter = yValueFormatter ), bottomAxis = HorizontalAxis.rememberBottom( rememberLineComponent(Fill.Transparent), tick = rememberLineComponent(Fill.Transparent), - guideline = rememberLineComponent(Fill.Transparent) + guideline = rememberLineComponent(Fill.Transparent), + valueFormatter = xValueFormatter ) ), modelProducer = modelProducer, diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/viewModel/StatsViewModel.kt b/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/viewModel/StatsViewModel.kt index 9a887e5..296456a 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/viewModel/StatsViewModel.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/statsScreen/viewModel/StatsViewModel.kt @@ -28,15 +28,18 @@ class StatsViewModel( private val allStatsSummary = statRepository.getLastWeekStatsSummary() private val averageFocusTimes = statRepository.getAverageFocusTimes() - val allStatsSummaryModelProducer = CartesianChartModelProducer() + val lastWeekSummaryChartModelProducer = CartesianChartModelProducer() val todayStatModelProducer = CartesianChartModelProducer() init { viewModelScope.launch(Dispatchers.IO) { allStatsSummary .collect { list -> - allStatsSummaryModelProducer.runTransaction { - columnSeries { series(list.reversed().map { it.focusTime }) } + lastWeekSummaryChartModelProducer.runTransaction { + columnSeries { + // reversing is required because we need ascending order while the DB returns descending order + series(list.reversed().map { it.focusTime }) + } } } } diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt index 2cad2ed..aafd9dd 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt @@ -85,13 +85,15 @@ class TimerViewModel( resetTimer() - var lastDate = LocalDate.parse(statRepository.getLastDate()) + var lastDate = statRepository.getLastDate() val today = LocalDate.now() // Fills dates between today and lastDate with 0s to ensure continuous history - while (lastDate.until(today).days > 0) { - lastDate = lastDate.plusDays(1) - statRepository.insertStat(Stat(lastDate.toString(), 0, 0, 0, 0, 0)) + lastDate?.until(today)?.days?.let { + while (it > 0) { + lastDate = lastDate?.plusDays(1) + statRepository.insertStat(Stat(lastDate!!, 0, 0, 0, 0, 0)) + } } delay(1500)