support ledger file append
parent
f32fb5aad1
commit
bbd51ea9c5
46
config.go
46
config.go
|
|
@ -11,6 +11,18 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Uploader int
|
||||||
|
|
||||||
|
const (
|
||||||
|
UploaderTodo = Uploader(iota)
|
||||||
|
UploaderLedger
|
||||||
|
)
|
||||||
|
|
||||||
|
var uploaders = map[string]Uploader{
|
||||||
|
"todo": UploaderTodo,
|
||||||
|
"ledger": UploaderLedger,
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
EmailUser string
|
EmailUser string
|
||||||
EmailPass string
|
EmailPass string
|
||||||
|
|
@ -19,6 +31,7 @@ type Config struct {
|
||||||
TodoToken string
|
TodoToken string
|
||||||
TodoList string
|
TodoList string
|
||||||
TodoTag string
|
TodoTag string
|
||||||
|
Uploader Uploader
|
||||||
Storage storage.DB
|
Storage storage.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,6 +43,9 @@ func NewConfig() Config {
|
||||||
as.Append(args.STRING, "emailuser", "email username", "breellocaldev@gmail.com")
|
as.Append(args.STRING, "emailuser", "email username", "breellocaldev@gmail.com")
|
||||||
as.Append(args.STRING, "emailpass", "email password", "ML3WQRFSqe9rQ8qNkm")
|
as.Append(args.STRING, "emailpass", "email password", "ML3WQRFSqe9rQ8qNkm")
|
||||||
as.Append(args.STRING, "emailimap", "email imap", "imap.gmail.com:993")
|
as.Append(args.STRING, "emailimap", "email imap", "imap.gmail.com:993")
|
||||||
|
|
||||||
|
as.Append(args.STRING, "uploader", "todo, ledger", "todo")
|
||||||
|
|
||||||
as.Append(args.STRING, "todoaddr", "todo addr", "https://todo-server.remote.blapointe.com")
|
as.Append(args.STRING, "todoaddr", "todo addr", "https://todo-server.remote.blapointe.com")
|
||||||
as.Append(args.STRING, "todopass", "todo pass", "gJtEXbbLHLf54yS9EdujtVN2n6Y")
|
as.Append(args.STRING, "todopass", "todo pass", "gJtEXbbLHLf54yS9EdujtVN2n6Y")
|
||||||
as.Append(args.STRING, "todotoken", "todo token", "")
|
as.Append(args.STRING, "todotoken", "todo token", "")
|
||||||
|
|
@ -46,14 +62,10 @@ func NewConfig() Config {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
token := as.GetString("todotoken")
|
uploader := as.GetString("uploader")
|
||||||
if len(token) == 0 {
|
ul, ok := uploaders[uploader]
|
||||||
token = getToken(as)
|
if !ok {
|
||||||
}
|
panic("invalid uploader: " + uploader)
|
||||||
|
|
||||||
list := as.GetString("todolist")
|
|
||||||
if len(list) == 0 {
|
|
||||||
list = getList(as, token)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
storage, err := storage.New(storage.TypeFromString(as.GetString("store")), as.GetString("storeaddr"), as.GetString("storeuser"), as.GetString("storepass"))
|
storage, err := storage.New(storage.TypeFromString(as.GetString("store")), as.GetString("storeaddr"), as.GetString("storeuser"), as.GetString("storepass"))
|
||||||
|
|
@ -66,11 +78,25 @@ func NewConfig() Config {
|
||||||
EmailPass: as.GetString("emailpass"),
|
EmailPass: as.GetString("emailpass"),
|
||||||
EmailIMAP: as.GetString("emailimap"),
|
EmailIMAP: as.GetString("emailimap"),
|
||||||
TodoAddr: as.GetString("todoaddr"),
|
TodoAddr: as.GetString("todoaddr"),
|
||||||
TodoToken: token,
|
|
||||||
TodoList: list,
|
|
||||||
TodoTag: as.GetString("todotag"),
|
TodoTag: as.GetString("todotag"),
|
||||||
Storage: storage,
|
Storage: storage,
|
||||||
|
Uploader: ul,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.Uploader == UploaderTodo {
|
||||||
|
token := as.GetString("todotoken")
|
||||||
|
if len(token) == 0 {
|
||||||
|
token = getToken(as)
|
||||||
|
}
|
||||||
|
|
||||||
|
list := as.GetString("todolist")
|
||||||
|
if len(list) == 0 {
|
||||||
|
list = getList(as, token)
|
||||||
|
}
|
||||||
|
config.TodoToken = token
|
||||||
|
config.TodoList = list
|
||||||
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
44
upload.go
44
upload.go
|
|
@ -1,15 +1,31 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"local/oauth2"
|
"local/oauth2"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Upload(config Config, transaction *Transaction) error {
|
func Upload(config Config, transaction *Transaction) error {
|
||||||
|
switch config.Uploader {
|
||||||
|
case UploaderTodo:
|
||||||
|
return uploadTodo(config, transaction)
|
||||||
|
case UploaderLedger:
|
||||||
|
return uploadLedger(config, transaction)
|
||||||
|
default:
|
||||||
|
return errors.New("not impl: uploader")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func uploadTodo(config Config, transaction *Transaction) error {
|
||||||
params := url.Values{
|
params := url.Values{
|
||||||
"list": {config.TodoList},
|
"list": {config.TodoList},
|
||||||
"title": {transaction.Format()},
|
"title": {transaction.Format()},
|
||||||
|
|
@ -32,3 +48,31 @@ func Upload(config Config, transaction *Transaction) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func uploadLedger(config Config, transaction *Transaction) error {
|
||||||
|
f, err := os.OpenFile(config.TodoAddr, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
amount, _ := strconv.ParseFloat(transaction.Amount, 32)
|
||||||
|
amount *= -1
|
||||||
|
remote := "Withdrawal:"
|
||||||
|
if amount > 0 {
|
||||||
|
remote = "Deposit:"
|
||||||
|
}
|
||||||
|
remote += strings.ReplaceAll(transaction.Account, " ", "")
|
||||||
|
fmt.Fprintf(f, "%-50s%-s\n", formatGMailDate(transaction.Date), transaction.Account)
|
||||||
|
fmt.Fprintf(f, "%-50s%-50s$%.2f\n", "", transaction.Bank, amount)
|
||||||
|
fmt.Fprintf(f, "%-50s%-s\n", "", remote)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatGMailDate(s string) string {
|
||||||
|
time, err := time.Parse("[Mon, 2 Jan 2006 15:04:05 -0700 (MST)]", s)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(s, err)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return time.Format("2006-01-02")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"local/storage"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUploadLedger(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
transaction Transaction
|
||||||
|
}{
|
||||||
|
"simple": {
|
||||||
|
transaction: Transaction{
|
||||||
|
ID: uuid.New().String(),
|
||||||
|
Bank: Chase,
|
||||||
|
Amount: "1.10",
|
||||||
|
Account: "account",
|
||||||
|
Date: "today",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, d := range cases {
|
||||||
|
c := d
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
config := Config{
|
||||||
|
TodoAddr: path.Join(t.TempDir(), name),
|
||||||
|
Uploader: UploaderLedger,
|
||||||
|
Storage: storage.NewMap(),
|
||||||
|
}
|
||||||
|
if err := uploadLedger(config, &c.transaction); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadFile(config.TodoAddr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("\n%s", b)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatGMailDate(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
input string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
"ok": {
|
||||||
|
input: "[Tue, 20 Jul 2021 23:46:00 -0400 (EDT)]",
|
||||||
|
want: "2021-07-20",
|
||||||
|
},
|
||||||
|
"bad": {
|
||||||
|
input: "2021-07-20",
|
||||||
|
want: "2021-07-20",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, d := range cases {
|
||||||
|
c := d
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
got := formatGMailDate(c.input)
|
||||||
|
if got != c.want {
|
||||||
|
t.Fatal(c.input, c.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue