Skip to content
This repository has been archived by the owner on Nov 5, 2024. It is now read-only.

Commit

Permalink
WIP: ParseStatus.kt
Browse files Browse the repository at this point in the history
  • Loading branch information
ILIYANGERMANOV committed Apr 15, 2023
1 parent 70d23dc commit d6194eb
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 32 deletions.
47 changes: 34 additions & 13 deletions app/src/main/java/com/ivy/wallet/ui/csv/CSVScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
Expand Down Expand Up @@ -121,9 +122,10 @@ private fun CSVRow(
row: CSVRow,
header: Boolean,
even: Boolean,
modifier: Modifier = Modifier,
) {
Row(
modifier = Modifier
modifier = modifier
.fillMaxWidth()
) {
row.values.forEach { value ->
Expand Down Expand Up @@ -158,6 +160,7 @@ private fun CSVCell(
private fun <M> LazyListScope.mappingRow(
columns: CSVRow,
mapping: ColumnMapping<M>,
status: MappingStatus,
onMapTo: (Int, String) -> Unit,
metadataContent: (@Composable (M) -> Unit)? = null,
) {
Expand All @@ -169,19 +172,27 @@ private fun <M> LazyListScope.mappingRow(
.border(
width = 2.dp,
color = when {
mapping.required && !mapping.success -> UI.colors.red
mapping.success -> UI.colors.green
mapping.required && !status.success -> UI.colors.red
status.success -> UI.colors.green
else -> UI.colors.medium
}
},
shape = RoundedCornerShape(4.dp),
)
.padding(vertical = 8.dp, horizontal = 4.dp)
.padding(vertical = 8.dp, horizontal = 8.dp)
) {
Text(
text = mapping.ivyColumn,
style = UI.typo.b1.colorAs(UI.colors.primary),
)
Spacer(modifier = Modifier.height(4.dp))
Text(text = mapping.helpInfo, style = UI.typo.c)
Spacer8()
Text(text = "Choose a column:", style = UI.typo.b2)
Spacer(modifier = Modifier.height(4.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState()),
verticalAlignment = Alignment.CenterVertically
) {
columns.values.forEachIndexed { index, column ->
Expand All @@ -200,9 +211,13 @@ private fun <M> LazyListScope.mappingRow(
metadataContent(mapping.metadata)
}

if (mapping.sampleValues.isNotEmpty()) {
if (status.sampleValues.isNotEmpty()) {
Spacer8()
CSVRow(row = CSVRow(mapping.sampleValues), header = false, even = true)
CSVRow(
modifier = Modifier.horizontalScroll(rememberScrollState()),
row = CSVRow(status.sampleValues),
header = false, even = true
)
}
}
}
Expand All @@ -226,6 +241,7 @@ fun LazyListScope.important(
mappingRow(
columns = columns,
mapping = importantFields.amount,
status = importantFields.amountStatus,
onMapTo = { index, name -> onEvent(CSVEvent.MapAmount(index, name)) },
metadataContent = { multiplier ->
AmountMetadata(multiplier = multiplier, onEvent = onEvent)
Expand All @@ -234,6 +250,7 @@ fun LazyListScope.important(
mappingRow(
columns = columns,
mapping = importantFields.type,
status = importantFields.typeStatus,
onMapTo = { index, name -> onEvent(CSVEvent.MapType(index, name)) },
metadataContent = {
TypeMetadata(metadata = it, onEvent = onEvent)
Expand All @@ -242,6 +259,7 @@ fun LazyListScope.important(
mappingRow(
columns = columns,
mapping = importantFields.date,
status = importantFields.dateStatus,
onMapTo = { index, name -> onEvent(CSVEvent.MapDate(index, name)) },
metadataContent = {
DateMetadataUI(metadata = it, onEvent = onEvent)
Expand All @@ -250,11 +268,13 @@ fun LazyListScope.important(
mappingRow(
columns = columns,
mapping = importantFields.account,
status = importantFields.accountStatus,
onMapTo = { index, name -> onEvent(CSVEvent.MapAccount(index, name)) },
)
mappingRow(
columns = columns,
mapping = importantFields.accountCurrency,
status = importantFields.accountCurrencyStatus,
onMapTo = { index, name -> onEvent(CSVEvent.MapAccountCurrency(index, name)) },
)
}
Expand Down Expand Up @@ -313,15 +333,15 @@ private fun TypeMetadata(
onEvent(CSVEvent.TypeMetaChange(newMeta))
}

LabelEqualsField(
LabelContainsField(
label = "Income",
value = metadata.income,
onValueChange = {
onTypeMetaEvent(metadata.copy(income = it))
}
)
Spacer8()
LabelEqualsField(
LabelContainsField(
label = "Expense",
value = metadata.expense,
onValueChange = {
Expand All @@ -330,7 +350,7 @@ private fun TypeMetadata(
)
Spacer8()
Text(text = "(optional)", style = UI.typo.c)
LabelEqualsField(
LabelContainsField(
label = "Transfer",
value = metadata.transfer ?: "",
onValueChange = {
Expand All @@ -340,15 +360,16 @@ private fun TypeMetadata(
}

@Composable
fun LabelEqualsField(
fun LabelContainsField(
label: String,
value: String,
onValueChange: (String) -> Unit,
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = label, color = UI.colors.primary)
Text(text = label, color = UI.colors.primary, style = UI.typo.nB1)
Text(text = " contains ", style = UI.typo.c)
Spacer8(horizontal = true)
TextField(value = value, onValueChange = onValueChange)
TextField(value = value, onValueChange = onValueChange, singleLine = true)
}
}
// endregion
Expand Down
10 changes: 9 additions & 1 deletion app/src/main/java/com/ivy/wallet/ui/csv/CSVState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ data class CSVState(

data class ImportantFields(
val amount: ColumnMapping<Int>,
val amountStatus: MappingStatus,
val type: ColumnMapping<TrnTypeMetadata>,
val typeStatus: MappingStatus,
val date: ColumnMapping<DateMetadata>,
val dateStatus: MappingStatus,
val account: ColumnMapping<Unit>,
val accountStatus: MappingStatus,
val accountCurrency: ColumnMapping<Unit>,
val accountCurrencyStatus: MappingStatus,
)

data class TrnTypeMetadata(
Expand Down Expand Up @@ -45,9 +50,12 @@ data class ColumnMapping<M>(
val helpInfo: String,
val name: String,
val index: Int,
val sampleValues: List<String>,
val metadata: M,
val required: Boolean,
)

data class MappingStatus(
val sampleValues: List<String>,
val success: Boolean,
)

Expand Down
171 changes: 153 additions & 18 deletions app/src/main/java/com/ivy/wallet/ui/csv/CSVViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ivy.wallet.domain.deprecated.logic.csv.IvyFileReader
import com.ivy.wallet.ui.csv.domain.mappingFailure
import com.ivy.wallet.ui.csv.domain.parseImportantStatus
import com.opencsv.CSVReaderBuilder
import com.opencsv.validators.LineValidator
import com.opencsv.validators.RowValidator
Expand All @@ -27,40 +29,173 @@ class CSVViewModel @Inject constructor(

private var columns by mutableStateOf<CSVRow?>(null)
private var csv by mutableStateOf<List<CSVRow>?>(null)
private var important by mutableStateOf<ImportantFields?>(null)
private var transfer by mutableStateOf<TransferFields?>(null)
private var optional by mutableStateOf<OptionalFields?>(null)
private var successPercent by mutableStateOf<Double?>(null)
private var failedRows by mutableStateOf<List<CSVRow>?>(null)

@Composable
fun uiState(): CSVState = CSVState(
columns = columns,
csv = csv,
important = important,
transfer = transfer,
optional = optional,
successPercent = successPercent,
failedRows = failedRows,
private var amount by mutableStateOf(
ColumnMapping(
ivyColumn = "Amount",
helpInfo = "The amount of the transactions, a positive number. Negative numbers will be made positive.",
name = "",
index = -1,
metadata = 1,
required = true,
)
)
private var type by mutableStateOf(
ColumnMapping(
ivyColumn = "Transaction Type",
helpInfo = """
The type of the transaction. Can be Income, Expense or a Transfer.
""".trimIndent(),
name = "",
index = -1,
required = true,
metadata = TrnTypeMetadata(
income = "",
expense = "",
transfer = null,
)
)
)
private var date by mutableStateOf(
ColumnMapping(
ivyColumn = "Date",
helpInfo = """
The date of the transaction. To help us parse it just tell us
whether the Date or the Month comes first.
""".trimIndent(),
name = "",
index = -1,
required = true,
metadata = DateMetadata.DateFirst
)
)
private var account by mutableStateOf(
ColumnMapping(
ivyColumn = "Account",
helpInfo = """
The account of the transaction.
""".trimIndent(),
name = "",
index = -1,
required = true,
metadata = Unit,
)
)
private var accountCurrency by mutableStateOf(
ColumnMapping(
ivyColumn = "Account Currency",
helpInfo = """
The currency of the account that made the transaction.
In Ivy Wallet, transactions don't have a currency but inherit
the ones from their account.
""".trimIndent(),
name = "",
index = -1,
required = true,
metadata = Unit,
)
)

@Composable
fun uiState(): CSVState {
return CSVState(
columns = columns,
csv = csv,
important = important(csv),
transfer = transfer,
optional = optional,
successPercent = successPercent,
failedRows = failedRows,
)
}

@Composable
private fun important(csv: List<CSVRow>?): ImportantFields? {
val failure = mappingFailure()
val importantFields = ImportantFields(
amount = amount,
amountStatus = failure,
type = type,
typeStatus = failure,
date = date,
dateStatus = failure,
account = account,
accountStatus = failure,
accountCurrency = accountCurrency,
accountCurrencyStatus = failure,
)

return if (csv != null) {
val status = parseImportantStatus(csv, importantFields)
importantFields.copy(
amountStatus = status.amountStatus,
typeStatus = status.typeStatus,
dateStatus = status.dateStatus,
accountStatus = status.accountStatus,
accountCurrencyStatus = status.accountCurrencyStatus,
)
} else null
}


private suspend fun handleEvent(event: CSVEvent) {
when (event) {
is CSVEvent.FilePicked -> handleFilePicked(event)
is CSVEvent.AmountMultiplier -> TODO()
is CSVEvent.DataMetaChange -> TODO()
is CSVEvent.MapAccount -> TODO()
is CSVEvent.MapAccountCurrency -> TODO()
is CSVEvent.MapAmount -> TODO()
is CSVEvent.MapDate -> TODO()
is CSVEvent.MapType -> TODO()
is CSVEvent.TypeMetaChange -> TODO()
is CSVEvent.AmountMultiplier -> {
amount = amount.copy(
metadata = event.multiplier,
)
}
is CSVEvent.DataMetaChange -> {
date = date.copy(
metadata = event.meta
)
}
is CSVEvent.MapAccount -> {
account = account.copy(
index = event.index,
name = event.name
)
}
is CSVEvent.MapAccountCurrency -> {
accountCurrency = accountCurrency.copy(
index = event.index,
name = event.name
)
}
is CSVEvent.MapAmount -> {
amount = amount.copy(
index = event.index,
name = event.name
)
}
is CSVEvent.MapDate -> {
date = date.copy(
index = event.index,
name = event.name
)
}
is CSVEvent.MapType -> {
type = type.copy(
index = event.index,
name = event.name
)
}
is CSVEvent.TypeMetaChange -> {
type = type.copy(
metadata = event.meta
)
}
}
}

private suspend fun handleFilePicked(event: CSVEvent.FilePicked) = withContext(Dispatchers.IO) {
csv = processFile(event.uri)
columns = csv?.firstOrNull()
}

private suspend fun processFile(
Expand Down
Loading

0 comments on commit d6194eb

Please sign in to comment.