From 1acb36913aa74a1a1c1344cb34bc145d0c0054e9 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sun, 21 Mar 2021 19:04:06 +0800 Subject: [PATCH] code refactor --- pkg/api/data_managements.go | 158 ++------------------------------ pkg/exporters/csv_file.go | 162 +++++++++++++++++++++++++++++++++ pkg/exporters/data_exporter.go | 9 ++ 3 files changed, 178 insertions(+), 151 deletions(-) create mode 100644 pkg/exporters/csv_file.go create mode 100644 pkg/exporters/data_exporter.go diff --git a/pkg/api/data_managements.go b/pkg/api/data_managements.go index 61fdc271..034032ed 100644 --- a/pkg/api/data_managements.go +++ b/pkg/api/data_managements.go @@ -7,6 +7,7 @@ import ( "github.com/mayswind/lab/pkg/core" "github.com/mayswind/lab/pkg/errs" + "github.com/mayswind/lab/pkg/exporters" "github.com/mayswind/lab/pkg/log" "github.com/mayswind/lab/pkg/models" "github.com/mayswind/lab/pkg/services" @@ -15,11 +16,10 @@ import ( ) const pageCountForDataExport = 1000 -const csvHeaderLine = "Time,Type,Category,Sub Category,Account,Amount,Account2,Account2 Amount,Tags,Comment\n" -const csvDataLineFormat = "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n" // DataManagementsApi represents data management api type DataManagementsApi struct { + exporter *exporters.CSVFileExporter tokens *services.TokenService users *services.UserService accounts *services.AccountService @@ -31,6 +31,7 @@ type DataManagementsApi struct { // Initialize a data management api singleton instance var ( DataManagements = &DataManagementsApi{ + exporter: &exporters.CSVFileExporter{}, tokens: services.Tokens, users: services.Users, accounts: services.Accounts, @@ -101,16 +102,16 @@ func (a *DataManagementsApi) ExportDataHandler(c *core.Context) ([]byte, string, maxTime = transactions[len(transactions)-1].TransactionTime - 1 } - result, err := a.getCSVFormatData(c, allTransactions, accountMap, categoryMap, tagMap, tagIndexs) + result, err := a.exporter.GetOutputContent(uid, allTransactions, accountMap, categoryMap, tagMap, tagIndexs) if err != nil { log.ErrorfWithRequestId(c, "[data_managements.ExportDataHandler] failed to get csv format exported data for \"uid:%d\", because %s", uid, err.Error()) return nil, "", errs.Or(err, errs.ErrOperationFailed) } - fileName := a.getFileName(c) + fileName := a.getFileName() - return []byte(result), fileName, nil + return result, fileName, nil } // ClearDataHandler deletes all user data @@ -163,152 +164,7 @@ func (a *DataManagementsApi) ClearDataHandler(c *core.Context) (interface{}, *er return true, nil } -func (a *DataManagementsApi) getCSVFormatData(c *core.Context, transactions []*models.Transaction, accountMap map[int64]*models.Account, categoryMap map[int64]*models.TransactionCategory, tagMap map[int64]*models.TransactionTag, allTagIndexs map[int64][]int64) (string, error) { - var ret strings.Builder - - ret.Grow(len(transactions) * 100) - ret.WriteString(csvHeaderLine) - - for i := 0; i < len(transactions); i++ { - transaction := transactions[i] - - if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN { - continue - } - - transactionTime := utils.FormatToLongDateTimeWithoutSecond(utils.ParseFromUnixTime(utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime))) - transactionType := a.getTransactionTypeName(c, transaction.Type) - category := a.getTransactionCategoryName(c, transaction.CategoryId, categoryMap) - subCategory := a.getTransactionSubCategoryName(c, transaction.CategoryId, categoryMap) - account := a.getAccountName(c, transaction.AccountId, accountMap) - amount := a.getDisplayAmount(c, transaction.Amount) - account2 := "" - account2Amount := "" - - if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { - account2 = a.getAccountName(c, transaction.RelatedAccountId, accountMap) - account2Amount = a.getDisplayAmount(c, transaction.RelatedAccountAmount) - } - - tags := a.getTags(c, transaction.TransactionId, allTagIndexs, tagMap) - comment := a.getComment(c, transaction.Comment) - - ret.WriteString(fmt.Sprintf(csvDataLineFormat, transactionTime, transactionType, category, subCategory, account, amount, account2, account2Amount, tags, comment)) - } - - return ret.String(), nil -} - -func (a *DataManagementsApi) getTransactionTypeName(c *core.Context, transactionDbType models.TransactionDbType) string { - if transactionDbType == models.TRANSACTION_DB_TYPE_MODIFY_BALANCE { - return "Balance Modification" - } else if transactionDbType == models.TRANSACTION_DB_TYPE_INCOME { - return "Income" - } else if transactionDbType == models.TRANSACTION_DB_TYPE_EXPENSE { - return "Expense" - } else if transactionDbType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transactionDbType == models.TRANSACTION_DB_TYPE_TRANSFER_IN { - return "Transfer" - } else { - return "" - } -} - -func (a *DataManagementsApi) getTransactionCategoryName(c *core.Context, categoryId int64, categoryMap map[int64]*models.TransactionCategory) string { - category, exists := categoryMap[categoryId] - - if !exists { - return "" - } - - if category.ParentCategoryId == 0 { - return category.Name - } - - parentCategory, exists := categoryMap[category.ParentCategoryId] - - if !exists { - return "" - } - - return parentCategory.Name -} - -func (a *DataManagementsApi) getTransactionSubCategoryName(c *core.Context, categoryId int64, categoryMap map[int64]*models.TransactionCategory) string { - category, exists := categoryMap[categoryId] - - if exists { - return category.Name - } else { - return "" - } -} - -func (a *DataManagementsApi) getAccountName(c *core.Context, accountId int64, accountMap map[int64]*models.Account) string { - account, exists := accountMap[accountId] - - if exists { - return account.Name - } else { - return "" - } -} - -func (a *DataManagementsApi) getDisplayAmount(c *core.Context, amount int64) string { - displayAmount := utils.Int64ToString(amount) - integer := utils.SubString(displayAmount, 0, len(displayAmount)-2) - decimals := utils.SubString(displayAmount, -2, 2) - - if integer == "" { - integer = "0" - } else if integer == "-" { - integer = "-0" - } - - if len(decimals) == 0 { - decimals = "00" - } else if len(decimals) == 1 { - decimals = "0" + decimals - } - - return integer + "." + decimals -} - -func (a *DataManagementsApi) getTags(c *core.Context, transactionId int64, allTagIndexs map[int64][]int64, tagMap map[int64]*models.TransactionTag) string { - tagIndexs, exists := allTagIndexs[transactionId] - - if !exists { - return "" - } - - var ret strings.Builder - - for i := 0; i < len(tagIndexs); i++ { - if i > 0 { - ret.WriteString(";") - } - - tagIndex := tagIndexs[i] - tag, exists := tagMap[tagIndex] - - if !exists { - continue - } - - ret.WriteString(tag.Name) - } - - return ret.String() -} - -func (a *DataManagementsApi) getComment(c *core.Context, comment string) string { - comment = strings.Replace(comment, ",", " ", -1) - comment = strings.Replace(comment, "\r\n", " ", -1) - comment = strings.Replace(comment, "\n", " ", -1) - - return comment -} - -func (a *DataManagementsApi) getFileName(c *core.Context) string { +func (a *DataManagementsApi) getFileName() string { currentTime := utils.FormatToLongDateTimeWithoutSecond(time.Now()) currentTime = strings.Replace(currentTime, "-", "_", -1) currentTime = strings.Replace(currentTime, " ", "_", -1) diff --git a/pkg/exporters/csv_file.go b/pkg/exporters/csv_file.go new file mode 100644 index 00000000..8d74504b --- /dev/null +++ b/pkg/exporters/csv_file.go @@ -0,0 +1,162 @@ +package exporters + +import ( + "fmt" + "strings" + + "github.com/mayswind/lab/pkg/models" + "github.com/mayswind/lab/pkg/utils" +) + +// CSVFileExporter defines the structure of csv file exporter +type CSVFileExporter struct { + DataExporter +} + +const csvHeaderLine = "Time,Type,Category,Sub Category,Account,Amount,Account2,Account2 Amount,Tags,Comment\n" +const csvDataLineFormat = "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n" + +func (e *CSVFileExporter) GetOutputContent(uid int64, transactions []*models.Transaction, accountMap map[int64]*models.Account, categoryMap map[int64]*models.TransactionCategory, tagMap map[int64]*models.TransactionTag, allTagIndexs map[int64][]int64) ([]byte, error) { + var ret strings.Builder + + ret.Grow(len(transactions) * 100) + ret.WriteString(csvHeaderLine) + + for i := 0; i < len(transactions); i++ { + transaction := transactions[i] + + if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_IN { + continue + } + + transactionTime := utils.FormatToLongDateTimeWithoutSecond(utils.ParseFromUnixTime(utils.GetUnixTimeFromTransactionTime(transaction.TransactionTime))) + transactionType := e.getTransactionTypeName(transaction.Type) + category := e.getTransactionCategoryName(transaction.CategoryId, categoryMap) + subCategory := e.getTransactionSubCategoryName(transaction.CategoryId, categoryMap) + account := e.getAccountName(transaction.AccountId, accountMap) + amount := e.getDisplayAmount(transaction.Amount) + account2 := "" + account2Amount := "" + + if transaction.Type == models.TRANSACTION_DB_TYPE_TRANSFER_OUT { + account2 = e.getAccountName(transaction.RelatedAccountId, accountMap) + account2Amount = e.getDisplayAmount(transaction.RelatedAccountAmount) + } + + tags := e.getTags(transaction.TransactionId, allTagIndexs, tagMap) + comment := e.getComment(transaction.Comment) + + ret.WriteString(fmt.Sprintf(csvDataLineFormat, transactionTime, transactionType, category, subCategory, account, amount, account2, account2Amount, tags, comment)) + } + + return []byte(ret.String()), nil +} + +func (e *CSVFileExporter) getTransactionTypeName( transactionDbType models.TransactionDbType) string { + if transactionDbType == models.TRANSACTION_DB_TYPE_MODIFY_BALANCE { + return "Balance Modification" + } else if transactionDbType == models.TRANSACTION_DB_TYPE_INCOME { + return "Income" + } else if transactionDbType == models.TRANSACTION_DB_TYPE_EXPENSE { + return "Expense" + } else if transactionDbType == models.TRANSACTION_DB_TYPE_TRANSFER_OUT || transactionDbType == models.TRANSACTION_DB_TYPE_TRANSFER_IN { + return "Transfer" + } else { + return "" + } +} + +func (e *CSVFileExporter) getTransactionCategoryName( categoryId int64, categoryMap map[int64]*models.TransactionCategory) string { + category, exists := categoryMap[categoryId] + + if !exists { + return "" + } + + if category.ParentCategoryId == 0 { + return category.Name + } + + parentCategory, exists := categoryMap[category.ParentCategoryId] + + if !exists { + return "" + } + + return parentCategory.Name +} + +func (e *CSVFileExporter) getTransactionSubCategoryName( categoryId int64, categoryMap map[int64]*models.TransactionCategory) string { + category, exists := categoryMap[categoryId] + + if exists { + return category.Name + } else { + return "" + } +} + +func (e *CSVFileExporter) getAccountName( accountId int64, accountMap map[int64]*models.Account) string { + account, exists := accountMap[accountId] + + if exists { + return account.Name + } else { + return "" + } +} + +func (e *CSVFileExporter) getDisplayAmount( amount int64) string { + displayAmount := utils.Int64ToString(amount) + integer := utils.SubString(displayAmount, 0, len(displayAmount)-2) + decimals := utils.SubString(displayAmount, -2, 2) + + if integer == "" { + integer = "0" + } else if integer == "-" { + integer = "-0" + } + + if len(decimals) == 0 { + decimals = "00" + } else if len(decimals) == 1 { + decimals = "0" + decimals + } + + return integer + "." + decimals +} + +func (e *CSVFileExporter) getTags( transactionId int64, allTagIndexs map[int64][]int64, tagMap map[int64]*models.TransactionTag) string { + tagIndexs, exists := allTagIndexs[transactionId] + + if !exists { + return "" + } + + var ret strings.Builder + + for i := 0; i < len(tagIndexs); i++ { + if i > 0 { + ret.WriteString(";") + } + + tagIndex := tagIndexs[i] + tag, exists := tagMap[tagIndex] + + if !exists { + continue + } + + ret.WriteString(tag.Name) + } + + return ret.String() +} + +func (e *CSVFileExporter) getComment( comment string) string { + comment = strings.Replace(comment, ",", " ", -1) + comment = strings.Replace(comment, "\r\n", " ", -1) + comment = strings.Replace(comment, "\n", " ", -1) + + return comment +} diff --git a/pkg/exporters/data_exporter.go b/pkg/exporters/data_exporter.go new file mode 100644 index 00000000..0f93a9a9 --- /dev/null +++ b/pkg/exporters/data_exporter.go @@ -0,0 +1,9 @@ +package exporters + +import "github.com/mayswind/lab/pkg/models" + +// DataExporter defines the structure of data exporter +type DataExporter interface { + // GetOutputContent returns the exported data + GetOutputContent(uid int64, transactions []*models.Transaction, accountMap map[int64]*models.Account, categoryMap map[int64]*models.TransactionCategory, tagMap map[int64]*models.TransactionTag, allTagIndexs map[int64][]int64) ([]byte, error) +}