diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/BackupRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/BackupRepository.kt index 948453887..ee2eda0c8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/BackupRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/BackupRepository.kt @@ -28,6 +28,7 @@ import org.koitharu.kotatsu.backups.data.model.HistoryBackup import org.koitharu.kotatsu.backups.data.model.MangaBackup import org.koitharu.kotatsu.backups.data.model.ScrobblingBackup import org.koitharu.kotatsu.backups.data.model.SourceBackup +import org.koitharu.kotatsu.backups.data.model.StatisticBackup import org.koitharu.kotatsu.backups.domain.BackupSection import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.prefs.AppSettings @@ -116,6 +117,12 @@ class BackupRepository @Inject constructor( data = database.getScrobblingDao().dumpEnabled().map { ScrobblingBackup(it) }, serializer = serializer(), ) + + BackupSection.STATS -> output.writeJsonArray( + section = BackupSection.STATS, + data = database.getStatsDao().dumpEnabled().map { StatisticBackup(it) }, + serializer = serializer(), + ) } progress?.emit(commonProgress) commonProgress++ @@ -174,6 +181,10 @@ class BackupRepository @Inject constructor( getScrobblingDao().upsert(it.toEntity()) } + BackupSection.STATS -> input.readJsonArray(serializer()).restoreToDb { + getStatsDao().upsert(it.toEntity()) + } + null -> CompositeResult.EMPTY // skip unknown entries } progress?.emit(commonProgress) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/model/StatisticBackup.kt b/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/model/StatisticBackup.kt new file mode 100644 index 000000000..af6309534 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/backups/data/model/StatisticBackup.kt @@ -0,0 +1,28 @@ +package org.koitharu.kotatsu.backups.data.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.koitharu.kotatsu.stats.data.StatsEntity + +@Serializable +class StatisticBackup( + @SerialName("manga_id") val mangaId: Long, + @SerialName("started_at") val startedAt: Long, + @SerialName("duration") val duration: Long, + @SerialName("pages") val pages: Int, +) { + + constructor(entity: StatsEntity) : this( + mangaId = entity.mangaId, + startedAt = entity.startedAt, + duration = entity.duration, + pages = entity.pages, + ) + + fun toEntity() = StatsEntity( + mangaId = mangaId, + startedAt = startedAt, + duration = duration, + pages = pages, + ) +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/backups/domain/BackupSection.kt b/app/src/main/kotlin/org/koitharu/kotatsu/backups/domain/BackupSection.kt index c5cfeb695..82aaf9e79 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/backups/domain/BackupSection.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/backups/domain/BackupSection.kt @@ -16,6 +16,7 @@ enum class BackupSection( BOOKMARKS("bookmarks"), SOURCES("sources"), SCROBBLING("scrobbling"), + STATS("statistics"), ; companion object { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/backups/ui/restore/BackupSectionModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/backups/ui/restore/BackupSectionModel.kt index a552d6d48..5865abb98 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/backups/ui/restore/BackupSectionModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/backups/ui/restore/BackupSectionModel.kt @@ -24,6 +24,7 @@ data class BackupSectionModel( BackupSection.BOOKMARKS -> R.string.bookmarks BackupSection.SOURCES -> R.string.remote_sources BackupSection.SCROBBLING -> R.string.tracking + BackupSection.STATS -> R.string.statistics } override fun areItemsTheSame(other: ListModel): Boolean { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/stats/data/StatsDao.kt b/app/src/main/kotlin/org/koitharu/kotatsu/stats/data/StatsDao.kt index c2a2ac4d3..87d6465d9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/stats/data/StatsDao.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/stats/data/StatsDao.kt @@ -7,8 +7,12 @@ import androidx.room.RawQuery import androidx.room.Upsert import androidx.sqlite.db.SimpleSQLiteQuery import androidx.sqlite.db.SupportSQLiteQuery +import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.isActive import org.koitharu.kotatsu.core.db.entity.MangaEntity +import kotlin.collections.forEach @Dao abstract class StatsDao { @@ -61,4 +65,19 @@ abstract class StatsDao { protected abstract suspend fun getDurationStatsImpl( query: SupportSQLiteQuery ): Map<@MapColumn("manga") MangaEntity, @MapColumn("d") Long> + + @Query("SELECT * FROM stats ORDER BY started_at LIMIT :limit OFFSET :offset") + protected abstract suspend fun findAll(offset: Int, limit: Int): List + fun dumpEnabled(): Flow = flow { + val window = 10 + var offset = 0 + while (currentCoroutineContext().isActive) { + val list = findAll(offset, window) + if (list.isEmpty()) { + break + } + offset += window + list.forEach { emit(it) } + } + } }