mirror of
https://github.com/andwati/ledga.git
synced 2026-10-18 03:12:55 +00:00
Add re-parse unparsed messages button
Settings > Data now has "Re-parse Unparsed Messages" button that re-runs the fixed parser on all UNKNOWN transactions, properly categorizing previously missed Fuliza auto-deductions, withdrawals, airtime, and reversals.
This commit is contained in:
@@ -102,4 +102,10 @@ interface TransactionDao {
|
||||
|
||||
@Query("SELECT COUNT(*) FROM transactions WHERE type = 'UNKNOWN'")
|
||||
fun getUnparsedCount(): Flow<Int>
|
||||
|
||||
@Query("SELECT * FROM transactions WHERE type = 'UNKNOWN'")
|
||||
suspend fun getUnparsedSync(): List<TransactionEntity>
|
||||
|
||||
@Query("DELETE FROM transactions WHERE id = :id")
|
||||
suspend fun deleteById(id: Long)
|
||||
}
|
||||
|
||||
@@ -85,6 +85,35 @@ class TransactionRepository @Inject constructor(
|
||||
transactionDao.updateCategory(transactionId, categoryId)
|
||||
}
|
||||
|
||||
suspend fun reparseUnknownTransactions(): ReparseResult {
|
||||
val unknowns = transactionDao.getUnparsedSync()
|
||||
var fixed = 0
|
||||
var stillUnknown = 0
|
||||
|
||||
for (entity in unknowns) {
|
||||
val result = com.ledga.app.data.parser.MpesaSmsParser.parse(entity.rawSms, entity.timestamp)
|
||||
when (result) {
|
||||
is com.ledga.app.data.parser.ParseResult.Success -> {
|
||||
if (result.transaction.type != com.ledga.app.data.parser.TransactionType.UNKNOWN) {
|
||||
// Delete old UNKNOWN entry, insert re-parsed one
|
||||
transactionDao.deleteById(entity.id)
|
||||
insertTransaction(result.transaction)
|
||||
fixed++
|
||||
} else {
|
||||
stillUnknown++
|
||||
}
|
||||
}
|
||||
is com.ledga.app.data.parser.ParseResult.Failure -> {
|
||||
// Balance checks etc get filtered — delete the UNKNOWN entry
|
||||
transactionDao.deleteById(entity.id)
|
||||
fixed++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ReparseResult(total = unknowns.size, fixed = fixed, stillUnknown = stillUnknown)
|
||||
}
|
||||
|
||||
private suspend fun autoCategorize(parsed: ParsedTransaction): Long? {
|
||||
// 1. Type-based defaults
|
||||
val typeDefault = when (parsed.type) {
|
||||
@@ -130,3 +159,5 @@ class TransactionRepository @Inject constructor(
|
||||
return typeDefault ?: 13L // Default to "Other"
|
||||
}
|
||||
}
|
||||
|
||||
data class ReparseResult(val total: Int, val fixed: Int, val stillUnknown: Int)
|
||||
|
||||
@@ -45,6 +45,7 @@ fun SettingsScreen(
|
||||
val largeTxnAlert by viewModel.largeTxnAlertEnabled.collectAsState()
|
||||
val unparsedCount by viewModel.unparsedCount.collectAsState()
|
||||
val importStatus by viewModel.importStatus.collectAsState()
|
||||
val reparseResult by viewModel.reparseResult.collectAsState()
|
||||
val exportResult by viewModel.exportResult.collectAsState()
|
||||
val fileImportResult by viewModel.fileImportResult.collectAsState()
|
||||
|
||||
@@ -160,8 +161,22 @@ fun SettingsScreen(
|
||||
}
|
||||
|
||||
if (unparsedCount > 0) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedButton(
|
||||
onClick = { viewModel.reparseUnknown() },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("Re-parse Unparsed Messages ($unparsedCount)")
|
||||
}
|
||||
if (reparseResult != null) {
|
||||
Text(
|
||||
text = "Unparsed Messages ($unparsedCount)",
|
||||
text = "Fixed ${reparseResult!!.fixed} of ${reparseResult!!.total} (${reparseResult!!.stillUnknown} still unknown)",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = "View Unparsed Messages",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.ledga.app.data.repository.ExportResult
|
||||
import com.ledga.app.data.repository.FontScale
|
||||
import com.ledga.app.data.repository.ImportFromFileResult
|
||||
import com.ledga.app.data.repository.ImportResult
|
||||
import com.ledga.app.data.repository.ReparseResult
|
||||
import com.ledga.app.data.repository.SettingsRepository
|
||||
import com.ledga.app.data.repository.SmsImporter
|
||||
import com.ledga.app.data.repository.TransactionRepository
|
||||
@@ -99,6 +100,15 @@ class SettingsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private val _reparseResult = MutableStateFlow<ReparseResult?>(null)
|
||||
val reparseResult: StateFlow<ReparseResult?> = _reparseResult
|
||||
|
||||
fun reparseUnknown() {
|
||||
viewModelScope.launch {
|
||||
_reparseResult.value = transactionRepository.reparseUnknownTransactions()
|
||||
}
|
||||
}
|
||||
|
||||
private val _exportResult = MutableStateFlow<ExportResult?>(null)
|
||||
val exportResult: StateFlow<ExportResult?> = _exportResult
|
||||
|
||||
|
||||
Reference in New Issue
Block a user