Implement intelligent merging for duplicate vocabulary items
This commit is contained in:
@@ -335,7 +335,6 @@ class VocabularyViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun deleteVocabularyItemsById(list: List<Int>) {
|
fun deleteVocabularyItemsById(list: List<Int>) {
|
||||||
Log.d(TAG, "Deleting vocabulary items with IDs: $list")
|
Log.d(TAG, "Deleting vocabulary items with IDs: $list")
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -407,18 +406,116 @@ class VocabularyViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the merging of two duplicate vocabulary items.
|
* Handles the merging of two duplicate vocabulary items intelligently.
|
||||||
* Placeholder logic: Deletes the new item, effectively keeping the original.
|
* Merges learning progress (stages, counts), categories, and features.
|
||||||
* A full implementation would merge properties like stage, categories, and features.
|
* Keeps the existing item and updates it with merged data.
|
||||||
|
*
|
||||||
|
* @param newItem The duplicate item to merge (will be deleted)
|
||||||
|
* @param existingItem The original item to keep and update
|
||||||
*/
|
*/
|
||||||
fun mergeDuplicateItems(newItem: VocabularyItem, existingItem: VocabularyItem) {
|
fun mergeDuplicateItems(newItem: VocabularyItem, existingItem: VocabularyItem) {
|
||||||
Log.d(TAG, "mergeDuplicateItems: Merging ${newItem.id} into ${existingItem.id}. (Placeholder logic)")
|
Log.d(TAG, "mergeDuplicateItems: Intelligently merging ${newItem.id} into ${existingItem.id}")
|
||||||
//TODO
|
|
||||||
// Placeholder: For now, this just deletes the new item.
|
|
||||||
// A full implementation would merge stages, categories, etc., and update the existing item based on the rules.
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
statusService.showSuccessMessage("Items merged!", 2)
|
try {
|
||||||
deleteVocabularyItemsById(listOf(newItem.id))
|
// 1. Get states for both items
|
||||||
|
val newState = vocabularyRepository.getVocabularyItemStateById(newItem.id)
|
||||||
|
val existingState = vocabularyRepository.getVocabularyItemStateById(existingItem.id)
|
||||||
|
|
||||||
|
// 2. Get stages for both items
|
||||||
|
val newStage = vocabularyRepository.getVocabularyItemStage(newItem.id).first()
|
||||||
|
val existingStage = vocabularyRepository.getVocabularyItemStage(existingItem.id).first()
|
||||||
|
|
||||||
|
// 3. Merge learning progress - keep the better stage (higher in progression)
|
||||||
|
val stageOrder = listOf(
|
||||||
|
VocabularyStage.NEW, VocabularyStage.STAGE_1, VocabularyStage.STAGE_2,
|
||||||
|
VocabularyStage.STAGE_3, VocabularyStage.STAGE_4, VocabularyStage.STAGE_5,
|
||||||
|
VocabularyStage.LEARNED
|
||||||
|
)
|
||||||
|
val mergedStage = if (stageOrder.indexOf(newStage) > stageOrder.indexOf(existingStage)) {
|
||||||
|
newStage
|
||||||
|
} else {
|
||||||
|
existingStage
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Merge answer counts and timestamps - keep higher counts and most recent timestamps
|
||||||
|
val mergedCorrectCount = (newState?.correctAnswerCount ?: 0) + (existingState?.correctAnswerCount ?: 0)
|
||||||
|
val mergedIncorrectCount = (newState?.incorrectAnswerCount ?: 0) + (existingState?.incorrectAnswerCount ?: 0)
|
||||||
|
|
||||||
|
val mergedLastCorrect = listOfNotNull(
|
||||||
|
newState?.lastCorrectAnswer,
|
||||||
|
existingState?.lastCorrectAnswer
|
||||||
|
).maxOrNull()
|
||||||
|
|
||||||
|
val mergedLastIncorrect = listOfNotNull(
|
||||||
|
newState?.lastIncorrectAnswer,
|
||||||
|
existingState?.lastIncorrectAnswer
|
||||||
|
).maxOrNull()
|
||||||
|
|
||||||
|
// 5. Merge features - prefer non-null, non-empty features
|
||||||
|
val mergedFeatures = when {
|
||||||
|
!existingItem.features.isNullOrBlank() -> existingItem.features
|
||||||
|
!newItem.features.isNullOrBlank() -> newItem.features
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Merge zipf frequencies - prefer non-null values
|
||||||
|
val mergedZipfFirst = existingItem.zipfFrequencyFirst ?: newItem.zipfFrequencyFirst
|
||||||
|
val mergedZipfSecond = existingItem.zipfFrequencySecond ?: newItem.zipfFrequencySecond
|
||||||
|
|
||||||
|
// 7. Keep the earlier creation date
|
||||||
|
val mergedCreatedAt = listOfNotNull(
|
||||||
|
newItem.createdAt,
|
||||||
|
existingItem.createdAt
|
||||||
|
).minOrNull() ?: existingItem.createdAt
|
||||||
|
|
||||||
|
// 8. Get categories for both items and merge them
|
||||||
|
val allMappings = vocabularyRepository.getCategoryMappings()
|
||||||
|
val newCategories = allMappings.filter { it.vocabularyItemId == newItem.id }.map { it.categoryId }
|
||||||
|
val existingCategories = allMappings.filter { it.vocabularyItemId == existingItem.id }.map { it.categoryId }
|
||||||
|
val mergedCategories = (newCategories + existingCategories).distinct()
|
||||||
|
|
||||||
|
// 9. Update the existing item with merged data
|
||||||
|
val updatedItem = existingItem.copy(
|
||||||
|
features = mergedFeatures,
|
||||||
|
zipfFrequencyFirst = mergedZipfFirst,
|
||||||
|
zipfFrequencySecond = mergedZipfSecond,
|
||||||
|
createdAt = mergedCreatedAt
|
||||||
|
)
|
||||||
|
|
||||||
|
vocabularyRepository.editVocabularyItem(updatedItem)
|
||||||
|
|
||||||
|
// 10. Update stage if it changed
|
||||||
|
if (mergedStage != existingStage) {
|
||||||
|
vocabularyRepository.changeVocabularyItemStage(updatedItem, mergedStage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11. Update state with merged progress
|
||||||
|
val mergedState = eu.gaudian.translator.model.VocabularyItemState(
|
||||||
|
vocabularyItemId = existingItem.id,
|
||||||
|
lastCorrectAnswer = mergedLastCorrect,
|
||||||
|
lastIncorrectAnswer = mergedLastIncorrect,
|
||||||
|
correctAnswerCount = mergedCorrectCount,
|
||||||
|
incorrectAnswerCount = mergedIncorrectCount
|
||||||
|
)
|
||||||
|
vocabularyRepository.saveVocabularyItemState(mergedState)
|
||||||
|
|
||||||
|
// 12. Add any new categories to the existing item
|
||||||
|
val categoriesToAdd = mergedCategories - existingCategories.toSet()
|
||||||
|
categoriesToAdd.forEach { categoryId ->
|
||||||
|
vocabularyRepository.addVocabularyItemToList(existingItem.id, categoryId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 13. Finally, delete the duplicate item
|
||||||
|
deleteVocabularyItemsById(listOf(newItem.id))
|
||||||
|
|
||||||
|
statusService.showSuccessMessage("Items merged successfully! Progress and categories preserved.", 3)
|
||||||
|
Log.i(TAG, "mergeDuplicateItems: Successfully merged ${newItem.id} into ${existingItem.id}. " +
|
||||||
|
"Stage: $existingStage→$mergedStage, Correct: ${existingState?.correctAnswerCount ?: 0}→$mergedCorrectCount, " +
|
||||||
|
"Categories: ${existingCategories.size}→${mergedCategories.size}")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
statusService.showErrorMessage("Error merging items: ${e.message}")
|
||||||
|
Log.e(TAG, "Error in mergeDuplicateItems: ${e.message}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user