Skip to content

Commit

Permalink
add existing data skip for partial backup restore
Browse files Browse the repository at this point in the history
  • Loading branch information
Razeeman committed Nov 3, 2024
1 parent 683757f commit 83e2e37
Show file tree
Hide file tree
Showing 14 changed files with 679 additions and 299 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.example.util.simpletimetracker.data_local.repo.RecordTypeToDefaultTag
import com.example.util.simpletimetracker.data_local.repo.RecordTypeToTagRepoImpl
import com.example.util.simpletimetracker.data_local.repo.RunningRecordRepoImpl
import com.example.util.simpletimetracker.data_local.repo.RunningRecordToRecordTagRepoImpl
import com.example.util.simpletimetracker.data_local.resolver.BackupPartialRepoImpl
import com.example.util.simpletimetracker.data_local.resolver.BackupRepoImpl
import com.example.util.simpletimetracker.data_local.resolver.CsvRepoImpl
import com.example.util.simpletimetracker.data_local.resolver.IcsRepoImpl
Expand All @@ -42,6 +43,7 @@ import com.example.util.simpletimetracker.domain.repo.RecordTypeToDefaultTagRepo
import com.example.util.simpletimetracker.domain.repo.RecordTypeToTagRepo
import com.example.util.simpletimetracker.domain.repo.RunningRecordRepo
import com.example.util.simpletimetracker.domain.repo.RunningRecordToRecordTagRepo
import com.example.util.simpletimetracker.domain.resolver.BackupPartialRepo
import com.example.util.simpletimetracker.domain.resolver.BackupRepo
import com.example.util.simpletimetracker.domain.resolver.CsvRepo
import com.example.util.simpletimetracker.domain.resolver.IcsRepo
Expand Down Expand Up @@ -76,6 +78,10 @@ interface DataLocalModuleBinds {
@Singleton
fun bindBackupRepo(impl: BackupRepoImpl): BackupRepo

@Binds
@Singleton
fun bindBackupPartialRepo(impl: BackupPartialRepoImpl): BackupPartialRepo

@Binds
@Singleton
fun bindCsvRepo(impl: CsvRepoImpl): CsvRepo
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import com.example.util.simpletimetracker.domain.model.DayOfWeek
import com.example.util.simpletimetracker.domain.model.FavouriteColor
import com.example.util.simpletimetracker.domain.model.FavouriteComment
import com.example.util.simpletimetracker.domain.model.FavouriteIcon
import com.example.util.simpletimetracker.domain.model.PartialBackupRestoreData
import com.example.util.simpletimetracker.domain.model.Record
import com.example.util.simpletimetracker.domain.model.RecordTag
import com.example.util.simpletimetracker.domain.model.RecordToRecordTag
Expand Down Expand Up @@ -216,218 +215,7 @@ class BackupRepoImpl @Inject constructor(
)
}

// Replace original id with 0 to add instead of replacing.
// Replace original ids in other data with actual id after adding.
override suspend fun partialRestoreBackupFile(
params: BackupOptionsData.Custom,
): ResultCode = withContext(Dispatchers.IO) {
val originalTypeIdToAddedId: MutableMap<Long, Long> = mutableMapOf()
val originalCategoryIdToAddedId: MutableMap<Long, Long> = mutableMapOf()
val originalTagIdToAddedId: MutableMap<Long, Long> = mutableMapOf()
val originalRecordIdToAddedId: MutableMap<Long, Long> = mutableMapOf()

params.data.types.values.forEach { type ->
val originalId = type.id
val addedId = type.copy(
id = 0,
).let { recordTypeRepo.add(it) }
originalTypeIdToAddedId[originalId] = addedId
}
params.data.records.values.forEach { record ->
val originalId = record.id
val newTypeId = originalTypeIdToAddedId[record.typeId]
?: return@forEach
val addedId = record.copy(
id = 0,
typeId = newTypeId,
).let { recordRepo.add(it) }
originalRecordIdToAddedId[originalId] = addedId
}
params.data.categories.values.forEach {
val originalId = it.id
val addedId = it.copy(
id = 0,
).let { categoryRepo.add(it) }
originalCategoryIdToAddedId[originalId] = addedId
}
params.data.typeToCategory.forEach { typeToCategory ->
val newTypeId = originalTypeIdToAddedId[typeToCategory.recordTypeId]
?: return@forEach
val newCategoryId = originalCategoryIdToAddedId[typeToCategory.categoryId]
?: return@forEach
typeToCategory.copy(
recordTypeId = newTypeId,
categoryId = newCategoryId,
).let { recordTypeCategoryRepo.add(it) }
}
params.data.tags.values.forEach { tag ->
val originalId = tag.id
val newColorSource = originalTypeIdToAddedId[tag.iconColorSource].orZero()
val addedId = tag.copy(
id = 0,
iconColorSource = newColorSource,
).let { recordTagRepo.add(it) }
originalTagIdToAddedId[originalId] = addedId
}
params.data.recordToTag.forEach { recordToTag ->
val newRecordId = originalRecordIdToAddedId[recordToTag.recordId]
?: return@forEach
val newTagId = originalTagIdToAddedId[recordToTag.recordTagId]
?: return@forEach
recordToTag.copy(
recordId = newRecordId,
recordTagId = newTagId,
).let { recordToRecordTagRepo.add(it) }
}
params.data.typeToTag.forEach { typeToTag ->
val newTypeId = originalTypeIdToAddedId[typeToTag.recordTypeId]
?: return@forEach
val newTagId = originalTagIdToAddedId[typeToTag.tagId]
?: return@forEach
typeToTag.copy(
recordTypeId = newTypeId,
tagId = newTagId,
).let { recordTypeToTagRepo.add(it) }
}
params.data.typeToDefaultTag.forEach { typeToDefaultTag ->
val newTypeId = originalTypeIdToAddedId[typeToDefaultTag.recordTypeId]
?: return@forEach
val newTagId = originalTagIdToAddedId[typeToDefaultTag.tagId]
?: return@forEach
typeToDefaultTag.copy(
recordTypeId = newTypeId,
tagId = newTagId,
).let { recordTypeToDefaultTagRepo.add(it) }
}
params.data.activityFilters.values.forEach { activityFilter ->
val newTypeIds = activityFilter.selectedIds
.mapNotNull { originalTypeIdToAddedId[it] }
activityFilter.copy(
id = 0,
selectedIds = newTypeIds,
).let { activityFilterRepo.add(it) }
}
params.data.favouriteComments.values.forEach { favComment ->
favComment.copy(
id = 0,
).let { favouriteCommentRepo.add(it) }
}
params.data.favouriteColors.values.forEach { favColor ->
favColor.copy(
id = 0,
).let { favouriteColorRepo.add(it) }
}
params.data.favouriteIcon.values.forEach { favIcon ->
favIcon.copy(
id = 0,
).let { favouriteIconRepo.add(it) }
}
params.data.goals.values.forEach { goal ->
val newId = when (val idData = goal.idData) {
is RecordTypeGoal.IdData.Type -> originalTypeIdToAddedId[idData.value]
?.let(RecordTypeGoal.IdData::Type)
is RecordTypeGoal.IdData.Category -> originalCategoryIdToAddedId[idData.value]
?.let(RecordTypeGoal.IdData::Category)
} ?: return@forEach
goal.copy(
id = 0,
idData = newId,
).let { recordTypeGoalRepo.add(it) }
}
params.data.rules.values.forEach { rule ->
val newStartingTypeIds = rule.conditionStartingTypeIds
.mapNotNull { originalTypeIdToAddedId[it] }.toSet()
val newCurrentTypeIds = rule.conditionCurrentTypeIds
.mapNotNull { originalTypeIdToAddedId[it] }.toSet()
val newAssignTagIds = rule.actionAssignTagIds
.mapNotNull { originalTagIdToAddedId[it] }.toSet()
rule.copy(
id = 0,
actionAssignTagIds = newAssignTagIds,
conditionStartingTypeIds = newStartingTypeIds,
conditionCurrentTypeIds = newCurrentTypeIds,
).let { complexRuleRepo.add(it) }
}
return@withContext ResultCode.Success(
resourceRepo.getString(R.string.message_import_complete),
)
}

override suspend fun readBackupFile(
uriString: String,
): Pair<ResultCode, PartialBackupRestoreData?> = withContext(Dispatchers.IO) {
// Result data
val types: MutableList<RecordType> = mutableListOf()
val records: MutableList<Record> = mutableListOf()
val categories: MutableList<Category> = mutableListOf()
val typeToCategory: MutableList<RecordTypeCategory> = mutableListOf()
val tags: MutableList<RecordTag> = mutableListOf()
val recordToTag: MutableList<RecordToRecordTag> = mutableListOf()
val typeToTag: MutableList<RecordTypeToTag> = mutableListOf()
val typeToDefaultTag: MutableList<RecordTypeToDefaultTag> = mutableListOf()
val activityFilters: MutableList<ActivityFilter> = mutableListOf()
val favouriteComments: MutableList<FavouriteComment> = mutableListOf()
val favouriteColors: MutableList<FavouriteColor> = mutableListOf()
val favouriteIcon: MutableList<FavouriteIcon> = mutableListOf()
val goals: MutableList<RecordTypeGoal> = mutableListOf()
val rules: MutableList<ComplexRule> = mutableListOf()
val settings: MutableList<List<String>> = mutableListOf()

val result = readBackup(
uriString = uriString,
successCodeMessage = null,
errorCodeMessage = R.string.settings_file_open_error,
clearData = false,
migrateTags = {
tags += migrateTags(
types = types,
data = it,
)
},
dataHandler = DataHandler(
types = types::add,
records = records::add,
categories = categories::add,
typeToCategory = typeToCategory::add,
tags = tags::add,
recordToTag = recordToTag::add,
typeToTag = typeToTag::add,
typeToDefaultTag = typeToDefaultTag::add,
activityFilters = activityFilters::add,
favouriteComments = favouriteComments::add,
favouriteColors = favouriteColors::add,
favouriteIcon = favouriteIcon::add,
goals = goals::add,
rules = rules::add,
settings = settings::add,
),
)

val recordToTagIds = recordToTag.groupBy { it.recordId }
val processedRecords = records.map {
val thisTags = recordToTagIds[it.id].orEmpty()
it.copy(tagIds = thisTags.map(RecordToRecordTag::recordTagId))
}

result to PartialBackupRestoreData(
types = types.associateBy { it.id },
records = processedRecords.associateBy { it.id },
categories = categories.associateBy { it.id },
typeToCategory = typeToCategory,
tags = tags.associateBy { it.id },
recordToTag = recordToTag,
typeToTag = typeToTag,
typeToDefaultTag = typeToDefaultTag,
activityFilters = activityFilters.associateBy { it.id },
favouriteComments = favouriteComments.associateBy { it.id },
favouriteColors = favouriteColors.associateBy { it.id },
favouriteIcon = favouriteIcon.associateBy { it.id },
goals = goals.associateBy { it.id },
rules = rules.associateBy { it.id },
)
}

private suspend fun readBackup(
suspend fun readBackup(
uriString: String,
@StringRes successCodeMessage: Int?,
@StringRes errorCodeMessage: Int,
Expand Down Expand Up @@ -993,7 +781,7 @@ class BackupRepoImpl @Inject constructor(
)
}

private fun migrateTags(
fun migrateTags(
types: List<RecordType>,
data: List<Pair<RecordTag, Long>>,
): List<RecordTag> {
Expand Down Expand Up @@ -1025,7 +813,7 @@ class BackupRepoImpl @Inject constructor(
private fun String.restoreNewline() =
replace("", "\n")

private data class DataHandler(
data class DataHandler(
val types: suspend (RecordType) -> Unit,
val records: suspend (Record) -> Unit,
val categories: suspend (Category) -> Unit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package com.example.util.simpletimetracker.domain.interactor

import com.example.util.simpletimetracker.domain.model.BackupOptionsData
import com.example.util.simpletimetracker.domain.model.PartialBackupRestoreData
import com.example.util.simpletimetracker.domain.resolver.BackupPartialRepo
import com.example.util.simpletimetracker.domain.resolver.BackupRepo
import com.example.util.simpletimetracker.domain.resolver.ResultCode
import javax.inject.Inject

class BackupInteractor @Inject constructor(
private val backupRepo: BackupRepo,
private val backupPartialRepo: BackupPartialRepo,
private val externalViewsInteractor: UpdateExternalViewsInteractor,
) {

Expand All @@ -30,15 +32,15 @@ class BackupInteractor @Inject constructor(
suspend fun partialRestoreBackupFile(
params: BackupOptionsData.Custom,
): ResultCode {
val resultCode = backupRepo.partialRestoreBackupFile(params)
val resultCode = backupPartialRepo.partialRestoreBackupFile(params)
doAfterRestore()
return resultCode
}

suspend fun readBackupFileContent(
uriString: String,
): Pair<ResultCode, PartialBackupRestoreData?> {
return backupRepo.readBackupFile(uriString)
return backupPartialRepo.readBackupFile(uriString)
}

suspend fun doAfterRestore() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,32 @@ package com.example.util.simpletimetracker.domain.model

// TODO switch to LongObjectMap from androidx.collections
data class PartialBackupRestoreData(
val types: Map<Long, RecordType>,
val records: Map<Long, Record>,
val categories: Map<Long, Category>,
val typeToCategory: List<RecordTypeCategory>,
val tags: Map<Long, RecordTag>,
val recordToTag: List<RecordToRecordTag>,
val typeToTag: List<RecordTypeToTag>,
val typeToDefaultTag: List<RecordTypeToDefaultTag>,
val activityFilters: Map<Long, ActivityFilter>,
val favouriteComments: Map<Long, FavouriteComment>,
val favouriteColors: Map<Long, FavouriteColor>,
val favouriteIcon: Map<Long, FavouriteIcon>,
val goals: Map<Long, RecordTypeGoal>,
val rules: Map<Long, ComplexRule>,
)
val types: Map<Long, Holder<RecordType>>,
val records: Map<Long, Holder<Record>>,
val categories: Map<Long, Holder<Category>>,
val typeToCategory: List<Holder<RecordTypeCategory>>,
val tags: Map<Long, Holder<RecordTag>>,
val recordToTag: List<Holder<RecordToRecordTag>>,
val typeToTag: List<Holder<RecordTypeToTag>>,
val typeToDefaultTag: List<Holder<RecordTypeToDefaultTag>>,
val activityFilters: Map<Long, Holder<ActivityFilter>>,
val favouriteComments: Map<Long, Holder<FavouriteComment>>,
val favouriteColors: Map<Long, Holder<FavouriteColor>>,
val favouriteIcon: Map<Long, Holder<FavouriteIcon>>,
val goals: Map<Long, Holder<RecordTypeGoal>>,
val rules: Map<Long, Holder<ComplexRule>>,
) {

data class Holder<T>(
val exist: Boolean,
val data: T,
)
}

fun <T> Collection<PartialBackupRestoreData.Holder<T>>.getNotExistingValues(): List<T> {
return this.mapNotNull { if (!it.exist) it.data else null }
}

fun <T> Collection<PartialBackupRestoreData.Holder<T>>.getExistingValues(): List<T> {
return this.mapNotNull { if (it.exist) it.data else null }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.util.simpletimetracker.domain.resolver

import com.example.util.simpletimetracker.domain.model.BackupOptionsData
import com.example.util.simpletimetracker.domain.model.PartialBackupRestoreData

interface BackupPartialRepo {

suspend fun partialRestoreBackupFile(
params: BackupOptionsData.Custom,
): ResultCode

suspend fun readBackupFile(
uriString: String,
): Pair<ResultCode, PartialBackupRestoreData?>
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.example.util.simpletimetracker.domain.resolver

import com.example.util.simpletimetracker.domain.model.BackupOptionsData
import com.example.util.simpletimetracker.domain.model.PartialBackupRestoreData

interface BackupRepo {

Expand All @@ -14,12 +13,4 @@ interface BackupRepo {
uriString: String,
params: BackupOptionsData.Restore,
): ResultCode

suspend fun partialRestoreBackupFile(
params: BackupOptionsData.Custom,
): ResultCode

suspend fun readBackupFile(
uriString: String,
): Pair<ResultCode, PartialBackupRestoreData?>
}
Loading

0 comments on commit 83e2e37

Please sign in to comment.