Skip to content

Commit

Permalink
feat: support full match option (#50)
Browse files Browse the repository at this point in the history
* feat: support full match option (#49)
* doc: update WeChat config example
* test: add huobi test script
  • Loading branch information
z2z23n0 authored Feb 7, 2022
1 parent 25538b6 commit 16909ca
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 30 deletions.
52 changes: 37 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,14 @@ alipay:
- item: 相互宝
targetAccount: Expenses:Insurance

- method: 余额 # 余额/余额宝
- method: 余额
fullMatch: true
methodAccount: Assets:Alipay
- method: 余额宝
fullMatch: true
methodAccount: Assets:Alipay
- method: 招商银行(9876)
fullMatch: true
methodAccount: Assets:Bank:CN:CMB-9876:Savings

- type: 收入 # 其他转账收款
Expand Down Expand Up @@ -199,11 +204,11 @@ alipay:
`alipay` is the provider-specific configuration. Alipay provider has rules matching mechanism.

`alipay` 蚂蚁账单相关的配置。它提供基于规则的匹配。可以指定:
- `peer`(交易对方)的包含匹配
- `item`(商品说明)的包含匹配
- `type`(收/支)的包含匹配
- `method`(收/付款方式)的包含匹配
- `category`(交易分类)的包含匹配
- `peer`(交易对方)的完全/包含匹配
- `item`(商品说明)的完全/包含匹配
- `type`(收/支)的完全/包含匹配
- `method`(收/付款方式)的完全/包含匹配
- `category`(交易分类)的完全/包含匹配
- `time`(交易时间)的区间匹配。
> 交易时间可写为以下两种形式:
> - `11:00-13:00`
Expand All @@ -212,6 +217,8 @@ alipay:

在单条规则中可以使用分隔符(sep)填写多个关键字,在同一对象中,每个关键字之间是或的关系。

在单条规则中可以使用 `fullMatch` 来设置字符匹配规则,`true` 表示使用完全匹配(full match),`false` 表示使用包含匹配(partial match),不设置该项则默认使用包含匹配。

匹配成功则使用规则中定义的 `targetAccount` 、 `methodAccount` 等账户覆盖默认定义账户。

规则匹配的顺序是:从 `rules` 配置中的第一条开始匹配,如果匹配成功仍继续匹配。也就是后面的规则优先级要**高于**前面的规则。
Expand Down Expand Up @@ -272,6 +279,12 @@ wechat:
sep: ','
time: 16:30-21:30
targetAccount: Expenses:Food:Meal:Dinner
- peer: 餐厅
time: 23:55-00:10 # test T+1
targetAccount: Expenses:Food:Meal:MidNight
- peer: 餐厅
time: 23:50-00:05 # test T-1
targetAccount: Expenses:Food:Meal:MidNight
- peer: 房东
type: 支出
Expand All @@ -291,7 +304,11 @@ wechat:
- method: / # 一般为收入,存入零钱
methodAccount: Assets:Digital:Wechat:Cash
- method: 零钱 # 零钱/零钱通
- method: 零钱
fullMatch: true
methodAccount: Assets:Digital:Wechat:Cash
- method: 零钱通
fullMatch: true
methodAccount: Assets:Digital:Wechat:Cash
- method: 工商银行
methodAccount: Assets:Bank:CN:ICBC:Savings
Expand All @@ -309,11 +326,11 @@ wechat:
`wechat` is the provider-specific configuration. WeChat provider has rules matching mechanism.

`wechat` 是微信相关的配置。它提供基于规则的匹配。可以指定:
- `peer`(交易对方)的包含匹配
- `item`(商品名称)的包含匹配
- `type`(收/支)的包含匹配
- `txType`(交易类型)的包含匹配
- `method`(支付方式)的包含匹配
- `peer`(交易对方)的完全/包含匹配
- `item`(商品名称)的完全/包含匹配
- `type`(收/支)的完全/包含匹配
- `txType`(交易类型)的完全/包含匹配
- `method`(支付方式)的完全/包含匹配
- `time`(交易时间)的区间匹配。
> 交易时间可写为以下两种形式:
> - `11:00-13:00`
Expand All @@ -322,6 +339,8 @@ wechat:

在单条规则中可以使用分隔符(sep)填写多个关键字,在同一对象中,每个关键字之间是或的关系。

在单条规则中可以使用 `fullMatch` 来设置字符匹配规则,`true` 表示使用完全匹配(full match),`false` 表示使用包含匹配(partial match),不设置该项则默认使用包含匹配。

匹配成功则使用规则中定义的 `targetAccount` 、 `methodAccount` 等账户覆盖默认定义账户。

规则匹配的顺序是:从 `rules` 配置中的第一条开始匹配,如果匹配成功仍继续匹配。也就是后面的规则优先级要**高于**前面的规则。
Expand Down Expand Up @@ -354,6 +373,7 @@ huobi:
- item: BTC/USDT,BTC1S/USDT # multiple keywords with separator
type: 币币交易
txType: 买入
fullMatch: true
sep: ',' # define separator as a comma
cashAccount: Assets:Rule1:Cash
positionAccount: Assets:Rule1:Positions
Expand All @@ -368,9 +388,9 @@ huobi:
`huobi` is the provider-specific configuration. Huobi provider has rules matching mechanism.

`huobi` 是火币相关的配置。它提供基于规则的匹配。可以指定:
- `item`(交易对)的包含匹配
- `type`(交易类型)的包含匹配
- `txType`(交易方向)的包含匹配
- `item`(交易对)的完全/包含匹配
- `type`(交易类型)的完全/包含匹配
- `txType`(交易方向)的完全/包含匹配
- `time`(交易时间)的区间匹配。
> 交易时间可写为以下两种形式:
> - `11:00-13:00`
Expand All @@ -379,6 +399,8 @@ huobi:

在单条规则中可以使用分隔符(sep)填写多个关键字,在同一对象中,每个关键字之间是或的关系。

在单条规则中可以使用 `fullMatch` 来设置字符匹配规则,`true` 表示使用完全匹配(full match),`false` 表示使用包含匹配(partial match),不设置该项则默认使用包含匹配。

匹配成功则使用规则中定义的 `cashAccount`, `positionAccount`, `commissionAccount` 和 `pnlAccount` 覆盖默认定义。

规则匹配的顺序是:从 `rules` 配置中的第一条开始匹配,如果匹配成功仍继续匹配。也就是后面的规则优先级要**高于**前面的规则。
Expand Down
7 changes: 6 additions & 1 deletion example/alipay/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ alipay:
- item: 相互宝
targetAccount: Expenses:Insurance

- method: 余额 # 余额/余额宝
- method: 余额
fullMatch: true
methodAccount: Assets:Alipay
- method: 余额宝
fullMatch: true
methodAccount: Assets:Alipay
- method: 招商银行(9876)
fullMatch: true
methodAccount: Assets:Bank:CN:CMB-9876:Savings

- type: 收入 # 其他转账收款
Expand Down
1 change: 1 addition & 0 deletions example/huobi/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ huobi:
- item: BTC/USDT,BTC1S/USDT
type: 币币交易
txType: 买入
fullMatch: true
sep: ','
cashAccount: Assets:Rule1:Cash
positionAccount: Assets:Rule1:Positions
Expand Down
6 changes: 5 additions & 1 deletion example/wechat/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ wechat:

- method: / # 一般为收入,存入零钱
methodAccount: Assets:Digital:Wechat:Cash
- method: 零钱 # 零钱/零钱通
- method: 零钱
fullMatch: true
methodAccount: Assets:Digital:Wechat:Cash
- method: 零钱通
fullMatch: true
methodAccount: Assets:Digital:Wechat:Cash
- method: 工商银行
methodAccount: Assets:Bank:CN:ICBC:Savings
Expand Down
16 changes: 11 additions & 5 deletions pkg/analyser/alipay/alipay.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,20 +55,26 @@ func (a Alipay) GetAccounts(o *ir.Order, cfg *config.Config, target, provider st
if r.Separator != nil {
sep = *r.Separator
}

matchFunc := util.SplitFindContains
if r.FullMatch {
matchFunc = util.SplitFindEquals
}

if r.Peer != nil {
match = util.SplitFindContains(*r.Peer, o.Peer, sep, match)
match = matchFunc(*r.Peer, o.Peer, sep, match)
}
if r.Type != nil {
match = util.SplitFindContains(*r.Type, o.TxTypeOriginal, sep, match)
match = matchFunc(*r.Type, o.TxTypeOriginal, sep, match)
}
if r.Item != nil {
match = util.SplitFindContains(*r.Item, o.Item, sep, match)
match = matchFunc(*r.Item, o.Item, sep, match)
}
if r.Method != nil {
match = util.SplitFindContains(*r.Method, o.Method, sep, match)
match = matchFunc(*r.Method, o.Method, sep, match)
}
if r.Category != nil {
match = util.SplitFindContains(*r.Category, o.Category, sep, match)
match = matchFunc(*r.Category, o.Category, sep, match)
}
if r.Time != nil {
match, err = util.SplitFindTimeInterval(*r.Time, o.PayTime, match)
Expand Down
12 changes: 9 additions & 3 deletions pkg/analyser/huobi/huobi.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,20 @@ func (h Huobi) GetAccounts(o *ir.Order, cfg *config.Config, target, provider str
if r.Seperator != nil {
sep = *r.Seperator
}

matchFunc := util.SplitFindContains
if r.FullMatch {
matchFunc = util.SplitFindEquals
}

if r.Type != nil {
match = util.SplitFindContains(*r.Type, o.TypeOriginal, sep, match)
match = matchFunc(*r.Type, o.TypeOriginal, sep, match)
}
if r.TxType != nil {
match = util.SplitFindContains(*r.TxType, o.TxTypeOriginal, sep, match)
match = matchFunc(*r.TxType, o.TxTypeOriginal, sep, match)
}
if r.Item != nil {
match = util.SplitFindContains(*r.Item, o.Item, sep, match)
match = matchFunc(*r.Item, o.Item, sep, match)
}
if r.Time != nil {
match, err = util.SplitFindTimeInterval(*r.Time, o.PayTime, match)
Expand Down
16 changes: 11 additions & 5 deletions pkg/analyser/wechat/wechat.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,26 @@ func (w Wechat) GetAccounts(o *ir.Order, cfg *config.Config, target, provider st
if r.Seperator != nil {
sep = *r.Seperator
}

matchFunc := util.SplitFindContains
if r.FullMatch {
matchFunc = util.SplitFindEquals
}

if r.Peer != nil {
match = util.SplitFindContains(*r.Peer, o.Peer, sep, match)
match = matchFunc(*r.Peer, o.Peer, sep, match)
}
if r.Type != nil {
match = util.SplitFindContains(*r.Type, o.TxTypeOriginal, sep, match)
match = matchFunc(*r.Type, o.TxTypeOriginal, sep, match)
}
if r.TxType != nil {
match = util.SplitFindContains(*r.TxType, o.TypeOriginal, sep, match)
match = matchFunc(*r.TxType, o.TypeOriginal, sep, match)
}
if r.Method != nil {
match = util.SplitFindContains(*r.Method, o.Method, sep, match)
match = matchFunc(*r.Method, o.Method, sep, match)
}
if r.Item != nil {
match = util.SplitFindContains(*r.Item, o.Item, sep, match)
match = matchFunc(*r.Item, o.Item, sep, match)
}
if r.Time != nil {
match, err = util.SplitFindTimeInterval(*r.Time, o.PayTime, match)
Expand Down
1 change: 1 addition & 0 deletions pkg/provider/alipay/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ type Rule struct {
MethodAccount *string `mapstructure:"methodAccount,omitempty"`
TargetAccount *string `mapstructure:"targetAccount,omitempty"`
PnlAccount *string `mapstructure:"pnlAccount,omitempty"`
FullMatch bool `mapstructure:"fullMatch,omitempty"`
}
1 change: 1 addition & 0 deletions pkg/provider/huobi/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ type Rule struct {
PositionAccount *string `mapstructure:"positionAccount,omitempty"`
CommissionAccount *string `mapstructure:"commissionAccount,omitempty"`
PnlAccount *string `mapstructure:"pnlAccount,omitempty"`
FullMatch bool `mapstructure:"fullMatch,omitempty"`
}
1 change: 1 addition & 0 deletions pkg/provider/wechat/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ type Rule struct {
MethodAccount *string `mapstructure:"methodAccount,omitempty"`
TargetAccount *string `mapstructure:"targetAccount,omitempty"`
CommissionAccount *string `mapstructure:"commissionAccount,omitempty"`
FullMatch bool `mapstructure:"fullMatch,omitempty"`
}
13 changes: 13 additions & 0 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ func SplitFindContains(str, target, sep string, match bool) bool {
return isContain && match
}

func SplitFindEquals(str, target, sep string, match bool) bool {
ss := strings.Split(str, sep)
isEqual := false
for _, s := range ss {
if target == s {
isEqual = true
break
}
}

return isEqual && match
}

func SplitFindTimeInterval(timeStr string, targetTime time.Time, match bool) (bool, error) {
isContain := false

Expand Down
30 changes: 30 additions & 0 deletions test/huobi-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
#
# E2E test for huobi provider.

# set -x # debug
set -eo errexit

TEST_DIR=`dirname "$(realpath $0)"`
ROOT_DIR="$TEST_DIR/.."

make -f "$ROOT_DIR/Makefile" build
mkdir -p "$ROOT_DIR/test/output"

# generate huobi bills output in beancount format
"$ROOT_DIR/bin/double-entry-generator" translate \
--provider huobi \
--config "$ROOT_DIR/example/huobi/config.yaml" \
--output "$ROOT_DIR/test/output/test-huobi-output.beancount" \
"$ROOT_DIR/example/huobi/example-huobi-records.csv"

diff -u --color \
"$ROOT_DIR/example/huobi/example-huobi-output.beancount" \
"$ROOT_DIR/test/output/test-huobi-output.beancount"

if [ $? -ne 0 ]; then
echo "[FAIL] Huobi provider output is different from expected output."
exit 1
fi

echo "[PASS] All Huobi provider tests!"

0 comments on commit 16909ca

Please sign in to comment.