Skip to content

Commit

Permalink
Handle new revolut exported data format
Browse files Browse the repository at this point in the history
  • Loading branch information
stonish committed Oct 13, 2021
1 parent 9681a71 commit 8f00286
Showing 1 changed file with 42 additions and 88 deletions.
130 changes: 42 additions & 88 deletions transactions/revolutTransaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,122 +7,78 @@ import (
"time"
"strconv"
"math"
"regexp"
"github.com/shuston/banking/utils"
)

type revolutTransaction struct {
id string
realDate string
transactionType string
product string
startedDate time.Time
completedDate time.Time
description string
amount float64
category string
notes string
details string
fee float64
currency string
state string
source string
balance float64
isHidden bool
}

var re = regexp.MustCompile(`\"([0-9]){1,3},([0-9]){3}\.([0-9]){2}\"+`)

func NewRevolutTransaction(rawData string) *revolutTransaction {
t := new(revolutTransaction)
data := strings.Split(rawData, ",")

rawData = t.preProcessData(rawData)
data := t.splitData(rawData)
t := new(revolutTransaction)
t.transactionType = data[0]
t.product = data[1]

completedDate, err := time.Parse("2 Jan 2006", data[0])
startedDate, err := time.Parse("2006-01-02 15:04:05", data[2])
if err != nil {
log.Fatal("Could not parse date: ", err, "\nRaw data: ", rawData)
}
t.startedDate = startedDate

t.completedDate = completedDate
t.description = strings.Trim(data[1], " ")

paidOut := strings.Replace(data[2], ",", "", -1)
paidIn := strings.Replace(data[3], ",", "", -1)

if paidOut != "" {
t.amount, err = strconv.ParseFloat(paidOut, 64)
completedDateStr := data[3]
if completedDateStr != "" {
completedDate, err := time.Parse("2006-01-02 15:04:05", completedDateStr)
if err != nil {
log.Fatal("Could not parse date: ", err, "\nRaw data: ", rawData)
}
} else {
t.amount, err = strconv.ParseFloat(paidIn, 32)
if err != nil {
log.Fatal("Could not parse amount: ", err, "\nRaw data: ", rawData)
}
t.amount = -1 * t.amount
t.completedDate = completedDate
}

t.category = data[7]
t.notes = data[8]

t.details = t.description
if t.notes != "" {
t.details = t.details + " - " + t.notes
}

t.source = "Revolut"
t.isHidden = t.shouldHide()
t.id = utils.Classify(t.details)

return t
}
t.description = strings.Trim(data[4], " ")

func (t revolutTransaction) preProcessData(rawData string) string {
// Handle additional commas in Details
if strings.Contains(rawData, "\"\"") {
split := strings.Split(rawData, "\"\"")
split[1] = strings.Replace(split[1], ",", ";", -1)
rawData = strings.Join(split, "\"\"")
}

// Handle Fees
rawData = strings.Replace(rawData, ", Fee: ", "; Fee: ", -1)

// Handle amounts >= 1,000.00
matches := re.FindAllString(rawData, -1)
for _, match := range matches {
split := strings.Split(rawData, match)
match = strings.Replace(match, "\"", "", -1)
match = strings.Replace(match, ",", "", -1)
rawData = strings.Join(split, match)
amount, err := strconv.ParseFloat(data[5], 64)
if err != nil {
log.Fatal("Could not parse amount: ", err, "\nRaw data: ", rawData)
}

return rawData
}

func (t revolutTransaction) splitData(rawData string) []string {
// Split and trim raw data
data := strings.Split(rawData, ",")
for i := range data {
data[i] = strings.Trim(data[i], " ")
fee, err := strconv.ParseFloat(data[6], 64)
if err != nil {
log.Fatal("Could not parse fee: ", err, "\nRaw data: ", rawData)
}
t.fee = fee
t.amount = -amount + t.fee

// Check for unexpected / differently formatted data
if len(data) > 9 {
t.currency = data[7]
t.state = data[8]

// Attempt to recover for balances >= 1,000.00
if len(data) == 10 && data[9] == "" && data[8] != "" {
_, err1 := strconv.ParseInt(data[6], 10, 32)
_, err2 := strconv.ParseFloat(data[7], 32)
_, err3 := strconv.ParseFloat(data[8], 32) // This should fail

if err1 != nil || err2 != nil || err3 == nil {
log.Fatal("Unexpected format for input data.\nRaw data:", rawData)
}

data[6] = data[6] + data[7]
data[7] = data[8]
data[8] = ""
} else {
log.Fatal("Unexpected format for input data.\nRaw data:", rawData)
balanceStr := data[9]
if balanceStr != "" {
balance, err := strconv.ParseFloat(data[9], 64)
if err != nil {
log.Fatal("Could not parse balance: ", err, "\nRaw data: ", rawData)
}
t.balance = balance
}

return data
t.source = "Revolut"
t.isHidden = t.shouldHide()
t.id = utils.Classify(t.description)

return t
}

func (t revolutTransaction) GetCompletedDate() time.Time {
Expand All @@ -137,15 +93,13 @@ func (t revolutTransaction) Output() {
roundedAmount := math.Round(t.amount*100)/100
strAmount := fmt.Sprintf("%f", roundedAmount)
fmt.Println(t.id + "\t" +
t.realDate + "\t" +
t.startedDate.Format("2 Jan") + "\t" +
t.completedDate.Format("2 Jan") + "\t" +
strAmount + "\t" +
t.details + "\t" +
t.description + "\t" +
t.source)
}

func (t revolutTransaction) shouldHide() bool {
return t.details == "Top-Up by *5278" ||
strings.HasPrefix(t.details, "Money added via ··5278") ||
t.details == "Money added via Google Pay"
return t.transactionType == "TOPUP" || t.state == "PENDING"
}

0 comments on commit 8f00286

Please sign in to comment.