diff --git a/comdirect/cmd/balance.go b/comdirect/cmd/balance.go index d8a97d7..fec80fc 100644 --- a/comdirect/cmd/balance.go +++ b/comdirect/cmd/balance.go @@ -1,12 +1,12 @@ package cmd import ( - "encoding/csv" + "log" + "os" + "github.com/jsattler/go-comdirect/pkg/comdirect" "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" - "log" - "os" ) var ( @@ -39,12 +39,14 @@ func balance(cmd *cobra.Command, args []string) { } func printBalanceCSV(balances *comdirect.AccountBalances) { - table := csv.NewWriter(os.Stdout) - table.Write([]string{"ID", "TYPE", "IBAN", "BALANCE"}) - for _, a := range balances.Values { - table.Write([]string{a.AccountId, a.Account.AccountType.Text, a.Account.Iban, a.Balance.Value}) + t, err := getCSVTemplate("balance.tmpl") + if err != nil { + log.Fatal(err) + } + err = t.ExecuteTemplate(os.Stdout, t.Name(), balances) + if err != nil { + log.Fatal(err) } - table.Flush() } func printBalanceTable(account *comdirect.AccountBalances) { diff --git a/comdirect/cmd/depot.go b/comdirect/cmd/depot.go index 87176ba..013e510 100644 --- a/comdirect/cmd/depot.go +++ b/comdirect/cmd/depot.go @@ -1,11 +1,12 @@ package cmd import ( - "encoding/csv" + "log" + "os" + "github.com/jsattler/go-comdirect/pkg/comdirect" "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" - "os" ) var ( @@ -38,12 +39,14 @@ func depot(cmd *cobra.Command, args []string) { } func printDepotsCSV(depots *comdirect.Depots) { - table := csv.NewWriter(os.Stdout) - table.Write([]string{"DEPOT ID", "DISPLAY ID", "HOLDER NAME", "CLIENT ID"}) - for _, d := range depots.Values { - table.Write([]string{d.DepotId, d.DepotDisplayId, d.HolderName, d.ClientId}) + t, err := getCSVTemplate("depot.tmpl") + if err != nil { + log.Fatal(err) + } + err = t.ExecuteTemplate(os.Stdout, t.Name(), depots) + if err != nil { + log.Fatal(err) } - table.Flush() } func printDepotsTable(depots *comdirect.Depots) { table := tablewriter.NewWriter(os.Stdout) diff --git a/comdirect/cmd/document.go b/comdirect/cmd/document.go index 8078ad3..199a28e 100644 --- a/comdirect/cmd/document.go +++ b/comdirect/cmd/document.go @@ -2,13 +2,14 @@ package cmd import ( "fmt" - "github.com/jsattler/go-comdirect/pkg/comdirect" - "github.com/olekukonko/tablewriter" - "github.com/spf13/cobra" "log" "os" "strings" "time" + + "github.com/jsattler/go-comdirect/pkg/comdirect" + "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" ) var ( @@ -52,6 +53,8 @@ func document(cmd *cobra.Command, args []string) { printJSON(filtered) case "markdown": printDocumentTable(filtered) + case "csv": + printDocumentCSV(filtered) default: printDocumentTable(filtered) } @@ -68,13 +71,24 @@ func download(client *comdirect.Client, documents *comdirect.Documents) { log.Fatal("failed to download document: ", err) } // TODO: think about a better solution to limit download requests to 10/sec - if i % 10 == 0 && i != 0{ + if i%10 == 0 && i != 0 { time.Sleep(900 * time.Millisecond) } fmt.Printf("Download complete for document with ID %s\n", d.DocumentID) } } +func printDocumentCSV(documents *comdirect.Documents) { + t, err := getCSVTemplate("document.tmpl") + if err != nil { + log.Fatal(err) + } + err = t.ExecuteTemplate(os.Stdout, t.Name(), documents) + if err != nil { + log.Fatal(err) + } +} + func printDocumentTable(documents *comdirect.Documents) { table := tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{"ID", "NAME", "DATE", "OPENED", "TYPE"}) diff --git a/comdirect/cmd/position.go b/comdirect/cmd/position.go index ec82e70..f517e84 100644 --- a/comdirect/cmd/position.go +++ b/comdirect/cmd/position.go @@ -1,11 +1,12 @@ package cmd import ( - "encoding/csv" + "log" + "os" + "github.com/jsattler/go-comdirect/pkg/comdirect" "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" - "os" ) var ( @@ -39,12 +40,15 @@ func position(cmd *cobra.Command, args []string) { } func printPositionsCSV(positions *comdirect.DepotPositions) { - table := csv.NewWriter(os.Stdout) - table.Write([]string{"POSITION ID", "WKN", "QUANTITY", "CURRENT PRICE", "PREVDAY %", "PURCHASE %", "PURCHASE", "CURRENT"}) - for _, d := range positions.Values { - table.Write([]string{d.PositionId, d.Wkn, d.Quantity.Value, d.CurrentPrice.Price.Value, d.ProfitLossPrevDayRel, d.ProfitLossPurchaseRel, d.PurchaseValue.Value, d.CurrentValue.Value}) + t, err := getCSVTemplate("position.tmpl") + if err != nil { + log.Fatal(err) } - table.Flush() + err = t.ExecuteTemplate(os.Stdout, t.Name(), positions) + if err != nil { + log.Fatal(err) + } + } func printPositionsTable(depots *comdirect.DepotPositions) { diff --git a/comdirect/cmd/report.go b/comdirect/cmd/report.go index 8b00f67..485b9e1 100644 --- a/comdirect/cmd/report.go +++ b/comdirect/cmd/report.go @@ -1,11 +1,12 @@ package cmd import ( - "encoding/csv" + "log" + "os" + "github.com/jsattler/go-comdirect/pkg/comdirect" "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" - "os" ) var ( @@ -39,18 +40,14 @@ func report(cmd *cobra.Command, args []string) { } func printReportsCSV(reports *comdirect.Reports) { - table := csv.NewWriter(os.Stdout) - table.Write(reportsHeader) - for _, r := range reports.Values { - var balance string - if r.Balance.Balance.Value == "" { - balance = formatAmountValue(r.Balance.PrevDayValue) - } else { - balance = formatAmountValue(r.Balance.Balance) - } - table.Write([]string{r.ProductID, r.ProductType, balance}) + t, err := getCSVTemplate("report.tmpl") + if err != nil { + log.Fatal(err) + } + err = t.ExecuteTemplate(os.Stdout, t.Name(), reports) + if err != nil { + log.Fatal(err) } - table.Flush() } func printReportsTable(reports *comdirect.Reports) { diff --git a/comdirect/cmd/root.go b/comdirect/cmd/root.go index 2adc3b4..1c1b889 100644 --- a/comdirect/cmd/root.go +++ b/comdirect/cmd/root.go @@ -5,10 +5,13 @@ import ( "fmt" "log" "os" + "path/filepath" "strconv" + "text/template" "time" "github.com/jsattler/go-comdirect/comdirect/keychain" + "github.com/jsattler/go-comdirect/comdirect/tpl" "github.com/jsattler/go-comdirect/pkg/comdirect" "github.com/spf13/cobra" ) @@ -26,6 +29,7 @@ var ( passwordFlag string clientIDFlag string clientSecretFlag string + templateFlag string rootCmd = &cobra.Command{ Use: "comdirect", @@ -54,6 +58,7 @@ func init() { rootCmd.PersistentFlags().StringVarP(&formatFlag, "format", "f", "markdown", "output format (markdown, csv or json)") rootCmd.PersistentFlags().IntVarP(&timeoutFlag, "timeout", "t", 30, "timeout in seconds to validate session TAN (default 30sec)") rootCmd.PersistentFlags().StringVar(&excludeFlag, "exclude", "", "exclude field from response") + rootCmd.PersistentFlags().StringVar(&templateFlag, "template", "", "template file name to format csv output") rootCmd.AddCommand(documentCmd) rootCmd.AddCommand(depotCmd) @@ -108,3 +113,21 @@ func initClient() *comdirect.Client { } return comdirect.NewWithAuthentication(authentication) } + +func getCSVTemplate(tplName string) (*template.Template, error) { + if templateFlag != "" { + tplName = filepath.Base(templateFlag) + } + + t := template.New(tplName).Funcs( + template.FuncMap{ + "formatAmountValue": formatAmountValue, + "holderName": holderName, + }, + ) + + if templateFlag != "" { + return t.ParseFiles(templateFlag) + } + return t.ParseFS(tpl.Default, tplName) +} diff --git a/comdirect/cmd/transaction.go b/comdirect/cmd/transaction.go index 51a803b..a735558 100644 --- a/comdirect/cmd/transaction.go +++ b/comdirect/cmd/transaction.go @@ -1,7 +1,6 @@ package cmd import ( - "encoding/csv" "encoding/json" "fmt" "log" @@ -112,19 +111,16 @@ func printJSON(v interface{}) { } func printTransactionCSV(transactions *comdirect.AccountTransactions) { - table := csv.NewWriter(os.Stdout) - table.Write(transactionHeader) - for _, t := range transactions.Values { - holderName := t.Remitter.HolderName - if len(holderName) > 30 { - holderName = holderName[:30] - } else if holderName == "" { - holderName = "N/A" - } - table.Write([]string{holderName, t.Creditor.HolderName, t.BookingDate, t.BookingStatus, t.TransactionType.Text, formatAmountValue(t.Amount), t.Amount.Unit}) + t, err := getCSVTemplate("transaction.tmpl") + if err != nil { + log.Fatal(err) + } + err = t.ExecuteTemplate(os.Stdout, t.Name(), transactions) + if err != nil { + log.Fatal(err) } - table.Flush() } + func printTransactionTable(transactions *comdirect.AccountTransactions) { table := tablewriter.NewWriter(os.Stdout) table.SetHeader(transactionHeader) @@ -143,3 +139,12 @@ func printTransactionTable(transactions *comdirect.AccountTransactions) { } table.Render() } + +func holderName(holderName string) string { + if len(holderName) > 30 { + holderName = holderName[:30] + } else if holderName == "" { + holderName = "N/A" + } + return holderName +} diff --git a/comdirect/tpl/balance.tmpl b/comdirect/tpl/balance.tmpl new file mode 100644 index 0000000..5847980 --- /dev/null +++ b/comdirect/tpl/balance.tmpl @@ -0,0 +1,4 @@ +ID,TYPE,IBAN,BALANCE +{{- range .Values}} +{{.AccountId}},{{.Account.AccountType.Text}},{{.Account.Iban}},{{.Balance.Value}} +{{- end}} diff --git a/comdirect/tpl/depot.tmpl b/comdirect/tpl/depot.tmpl new file mode 100644 index 0000000..a8f4a04 --- /dev/null +++ b/comdirect/tpl/depot.tmpl @@ -0,0 +1,4 @@ +DEPOT ID,DISPLAY ID,HOLDER NAME,CLIENT ID +{{- range .Values}} +{{.DepotId}},{{.DepotDisplayId}},{{.HolderName}},{{.ClientId}} +{{- end}} diff --git a/comdirect/tpl/document.tmpl b/comdirect/tpl/document.tmpl new file mode 100644 index 0000000..f3ed73e --- /dev/null +++ b/comdirect/tpl/document.tmpl @@ -0,0 +1,4 @@ +ID,NAME,DATE,OPENED,TYPE +{{- range .Values}} +{{.DocumentID}},"{{.Name}}",{{.DateCreation}},{{.DocumentMetaData.AlreadyRead}},{{.MimeType}} +{{- end}} diff --git a/comdirect/tpl/position.tmpl b/comdirect/tpl/position.tmpl new file mode 100644 index 0000000..dc7d517 --- /dev/null +++ b/comdirect/tpl/position.tmpl @@ -0,0 +1,4 @@ +POSITION ID,WKN,QUANTITY,CURRENT PRICE,PREVDAY %,PURCHASE %,PURCHASE,CURRENT +{{- range .Values}} +{{.PositionId}},{{.Wkn}},{{.Quantity.Value}},{{.CurrentPrice.Price.Value}},{{.ProfitLossPrevDayRel}},{{.ProfitLossPurchaseRel}},{{.PurchaseValue.Value}},{{.CurrentValue.Value}} +{{- end}} diff --git a/comdirect/tpl/report.tmpl b/comdirect/tpl/report.tmpl new file mode 100644 index 0000000..1f612ed --- /dev/null +++ b/comdirect/tpl/report.tmpl @@ -0,0 +1,4 @@ +ID,TYPE,BALANCE +{{- range .Values}} +{{.ProductID}},{{.ProductType}},{{if eq .Balance.Balance.Value ""}}{{formatAmountValue .Balance.PrevDayValue}}{{else}}{{formatAmountValue .Balance.Balance}}{{end}} +{{- end}} diff --git a/comdirect/tpl/tpl.go b/comdirect/tpl/tpl.go new file mode 100644 index 0000000..c1b8fa7 --- /dev/null +++ b/comdirect/tpl/tpl.go @@ -0,0 +1,8 @@ +package tpl + +import ( + "embed" +) + +//go:embed *.tmpl +var Default embed.FS diff --git a/comdirect/tpl/transaction.tmpl b/comdirect/tpl/transaction.tmpl new file mode 100644 index 0000000..b6e1d2b --- /dev/null +++ b/comdirect/tpl/transaction.tmpl @@ -0,0 +1,4 @@ +REMITTER,DEPTOR,BOOKING DATE,STATUS,TYPE,VALUE,UNIT +{{- range .Values}} +"{{holderName .Remitter.HolderName}}","{{.Creditor.HolderName}}",{{.BookingDate}},{{.BookingStatus}},{{.TransactionType.Text}},{{formatAmountValue .Amount}},{{.Amount.Unit}} +{{- end}} diff --git a/go.mod b/go.mod index 0988199..b798ead 100644 --- a/go.mod +++ b/go.mod @@ -6,17 +6,17 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/spf13/cobra v1.7.0 github.com/zalando/go-keyring v0.2.3 - golang.org/x/term v0.11.0 + golang.org/x/term v0.12.0 golang.org/x/time v0.3.0 ) require ( - github.com/alessio/shellescape v1.4.1 // indirect + github.com/alessio/shellescape v1.4.2 // indirect github.com/danieljoos/wincred v1.2.0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/rivo/uniseg v0.3.1 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/rivo/uniseg v0.4.4 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/sys v0.12.0 // indirect ) diff --git a/go.sum b/go.sum index fd126bb..7236fe3 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,12 @@ github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0= +github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -11,25 +14,36 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.3.1 h1:SDPP7SHNl1L7KrEFCSJslJ/DM9DT02Nq2C61XrfHMmk= github.com/rivo/uniseg v0.3.1/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= +golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=