-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathextract.go
128 lines (107 loc) · 3.11 KB
/
extract.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package bca
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"regexp"
"time"
"github.com/PuerkitoBio/goquery"
"github.com/shopspring/decimal"
)
// Balance is data in balance inquiry
type Balance struct {
AccountNumber string `json:"accountNumber"`
Currency string `json:"currency"`
BalanceRaw string `json:"balanceRaw"`
Balance decimal.Decimal `json:"balance"`
}
// Entry is a row in statement view
type Entry struct {
Date time.Time `json:"date"`
Description string `json:"description"`
Payee string `json:"payee"`
Type string `json:"type"`
AmountRaw string `json:"balanceRaw"`
Amount decimal.Decimal `json:"balance"`
}
var (
blncCheck = regexp.MustCompile("INFORMASI REKENING - INFORMASI SALDO")
stmtCheck = regexp.MustCompile("INFORMASI REKENING - MUTASI REKENING")
commas = regexp.MustCompile("[,]+")
)
const (
blncSlctr = "#pagebody > span > table:nth-child(2) > tbody > tr > td:nth-child(2) > table > tbody > tr:nth-child(2)"
stmtSlctr = "#pagebody > span > table:nth-child(2) > tbody > tr:nth-child(2) > td:nth-child(2) > table > tbody"
)
func extractHTML(b []byte, v interface{}) (err error) {
reader := ioutil.NopCloser(bytes.NewBuffer(b))
defer reader.Close()
doc, err := goquery.NewDocumentFromReader(reader)
if err != nil {
return err
}
if blncCheck.Match(b) {
return store(extractBalanceInquiry(doc), v)
}
if stmtCheck.Match(b) {
return store(extractStatement(doc), v)
}
return fmt.Errorf("failed to extract HTML: %s %s", string(b[:]), b[:])
}
func extractBalanceInquiry(doc *goquery.Document) *Balance {
selector := doc.Find(blncSlctr).Children()
b := Balance{
AccountNumber: selector.First().Text(),
Currency: selector.Next().First().Text(),
BalanceRaw: selector.Next().Next().First().Text(),
}
b.Balance = convDec(b.BalanceRaw)
return &b
}
func extractStatement(doc *goquery.Document) []Entry {
selector := doc.Find(stmtSlctr).Children().Next()
entries := make([]Entry, 0)
selector.Each(func(i int, s *goquery.Selection) {
s = s.Children()
s = s.First()
timeRaw := s.Text()
s = s.Next()
desc := s.Contents().FilterFunction(func(i int, s *goquery.Selection) bool {
return !s.Is("br")
})
amountRaw := desc.Get(desc.Size() - 1).Data
entries = append(entries, Entry{
Date: convTime(timeRaw),
Description: desc.Slice(0, desc.Size()-3).Text(),
Payee: desc.Get(desc.Size() - 3).Data,
Type: s.Next().Text(),
AmountRaw: amountRaw,
Amount: convDec(amountRaw),
})
})
return entries
}
func convDec(raw string) (d decimal.Decimal) {
d, err := decimal.NewFromString(commas.ReplaceAllString(raw, ""))
if err != nil {
return
}
return d
}
func convTime(raw string) (t time.Time) {
// Use +0700 WIB
l, _ := time.LoadLocation("Asia/Jakarta")
t, err := time.ParseInLocation("02/01", raw, l)
if err != nil {
return
}
return t.AddDate(time.Now().Year(), 0, 0)
}
func store(data interface{}, v interface{}) error {
b, err := json.Marshal(data)
if err != nil {
return err
}
return json.Unmarshal(b, v)
}