Add column filter option to holdings command

pull/112/head^2
Miguel Mota 3 years ago
parent 6e3b9d41f1
commit 3c97b58e09
No known key found for this signature in database
GPG Key ID: 67EC1161588A00F9

@ -12,12 +12,14 @@ func HoldingsCmd() *cobra.Command {
var help bool var help bool
var total bool var total bool
var noCache bool var noCache bool
var noHeader bool
var config string var config string
var sortBy string var sortBy string
var sortDesc bool var sortDesc bool
var format string = "table" var format string = "table"
var humanReadable bool var humanReadable bool
var filter []string var filter []string
var cols []string
var convert string var convert string
holdingsCmd := &cobra.Command{ holdingsCmd := &cobra.Command{
@ -52,7 +54,9 @@ func HoldingsCmd() *cobra.Command {
HumanReadable: humanReadable, HumanReadable: humanReadable,
Format: format, Format: format,
Filter: filter, Filter: filter,
Cols: cols,
Convert: convert, Convert: convert,
NoHeader: noHeader,
}) })
}, },
} }
@ -61,11 +65,13 @@ func HoldingsCmd() *cobra.Command {
holdingsCmd.Flags().BoolVarP(&total, "total", "t", total, "Show total only") holdingsCmd.Flags().BoolVarP(&total, "total", "t", total, "Show total only")
holdingsCmd.Flags().BoolVarP(&noCache, "no-cache", "", noCache, "No cache") holdingsCmd.Flags().BoolVarP(&noCache, "no-cache", "", noCache, "No cache")
holdingsCmd.Flags().BoolVarP(&humanReadable, "human", "h", humanReadable, "Human readable output") holdingsCmd.Flags().BoolVarP(&humanReadable, "human", "h", humanReadable, "Human readable output")
holdingsCmd.Flags().BoolVarP(&noHeader, "no-header", "", noHeader, "Don't display header columns")
holdingsCmd.Flags().StringVarP(&config, "config", "c", "", fmt.Sprintf("Config filepath. (default %s)", cointop.DefaultConfigFilepath)) holdingsCmd.Flags().StringVarP(&config, "config", "c", "", fmt.Sprintf("Config filepath. (default %s)", cointop.DefaultConfigFilepath))
holdingsCmd.Flags().StringVarP(&sortBy, "sort-by", "s", sortBy, `Sort by column. Options are "name", "symbol", "price", "holdings", "balance", "24h"`) holdingsCmd.Flags().StringVarP(&sortBy, "sort-by", "s", sortBy, `Sort by column. Options are "name", "symbol", "price", "holdings", "balance", "24h"`)
holdingsCmd.Flags().BoolVarP(&sortDesc, "sort-desc", "d", sortDesc, "Sort in descending order") holdingsCmd.Flags().BoolVarP(&sortDesc, "sort-desc", "d", sortDesc, "Sort in descending order")
holdingsCmd.Flags().StringVarP(&format, "format", "", format, `Ouput format. Options are "table", "csv", "json"`) holdingsCmd.Flags().StringVarP(&format, "format", "", format, `Ouput format. Options are "table", "csv", "json"`)
holdingsCmd.Flags().StringSliceVarP(&filter, "filter", "", filter, `Filter portfolio entries by coin name or symbol, comma separated. Example: "btc,eth,doge"`) holdingsCmd.Flags().StringSliceVarP(&filter, "filter", "", filter, `Filter portfolio entries by coin name or symbol, comma separated without spaces. Example: "btc,eth,doge"`)
holdingsCmd.Flags().StringSliceVarP(&cols, "cols", "", cols, `Filter portfolio columns, comma separated without spaces. Example: "symbol,holdings,balance"`)
holdingsCmd.Flags().StringVarP(&convert, "convert", "f", convert, "The currency to convert to") holdingsCmd.Flags().StringVarP(&convert, "convert", "f", convert, "The currency to convert to")
return holdingsCmd return holdingsCmd

@ -591,7 +591,9 @@ type TablePrintOptions struct {
HumanReadable bool HumanReadable bool
Format string Format string
Filter []string Filter []string
Cols []string
Convert string Convert string
NoHeader bool
} }
// outputFormats is list of valid output formats // outputFormats is list of valid output formats
@ -628,8 +630,10 @@ func (ct *Cointop) PrintHoldingsTable(options *TablePrintOptions) error {
sortDesc := options.SortDesc sortDesc := options.SortDesc
format := options.Format format := options.Format
humanReadable := options.HumanReadable humanReadable := options.HumanReadable
filter := options.Filter filterCoins := options.Filter
filterCols := options.Cols
holdings := ct.GetPortfolioSlice() holdings := ct.GetPortfolioSlice()
noHeader := options.NoHeader
if format == "" { if format == "" {
format = "table" format = "table"
@ -651,10 +655,41 @@ func (ct *Cointop) PrintHoldingsTable(options *TablePrintOptions) error {
records := make([][]string, len(holdings)) records := make([][]string, len(holdings))
symbol := ct.CurrencySymbol() symbol := ct.CurrencySymbol()
headers := []string{"name", "symbol", "price", "holdings", "balance", "24h%", "%holdings"}
if len(filterCols) > 0 {
for _, col := range filterCols {
valid := false
for _, h := range headers {
if col == h {
valid = true
break
}
}
switch col {
case "amount":
return fmt.Errorf("did you mean %q?", "balance")
case "24H":
fallthrough
case "24H%":
fallthrough
case "24h":
fallthrough
case "24h_change":
return fmt.Errorf("did you mean %q?", "24h%")
case "percent_holdings":
return fmt.Errorf("did you mean %q?", "%holdings")
}
if !valid {
return fmt.Errorf("unsupported column value %q", col)
}
}
headers = filterCols
}
for i, entry := range holdings { for i, entry := range holdings {
if len(filter) > 0 { if len(filterCoins) > 0 {
found := false found := false
for _, item := range filter { for _, item := range filterCoins {
item = strings.ToLower(strings.TrimSpace(item)) item = strings.ToLower(strings.TrimSpace(item))
if strings.ToLower(entry.Symbol) == item || strings.ToLower(entry.Name) == item { if strings.ToLower(entry.Symbol) == item || strings.ToLower(entry.Name) == item {
found = true found = true
@ -671,35 +706,54 @@ func (ct *Cointop) PrintHoldingsTable(options *TablePrintOptions) error {
percentHoldings = 0 percentHoldings = 0
} }
if humanReadable { item := make([]string, len(headers))
records[i] = []string{ for i, header := range headers {
entry.Name, switch header {
entry.Symbol, case "name":
fmt.Sprintf("%s%s", symbol, humanize.Commaf(entry.Price)), item[i] = entry.Name
humanize.Commaf(entry.Holdings), case "symbol":
fmt.Sprintf("%s%s", symbol, humanize.Commaf(entry.Balance)), item[i] = entry.Symbol
fmt.Sprintf("%.2f%%", entry.PercentChange24H), case "price":
fmt.Sprintf("%.2f%%", percentHoldings), if humanReadable {
} item[i] = fmt.Sprintf("%s%s", symbol, humanize.Commaf(entry.Price))
} else { } else {
records[i] = []string{ item[i] = strconv.FormatFloat(entry.Price, 'f', -1, 64)
entry.Name, }
entry.Symbol, case "holdings":
strconv.FormatFloat(entry.Price, 'f', -1, 64), if humanReadable {
strconv.FormatFloat(entry.Holdings, 'f', -1, 64), item[i] = humanize.Commaf(entry.Holdings)
strconv.FormatFloat(entry.Balance, 'f', -1, 64), } else {
fmt.Sprintf("%.2f", entry.PercentChange24H), item[i] = strconv.FormatFloat(entry.Holdings, 'f', -1, 64)
fmt.Sprintf("%.2f", percentHoldings), }
case "balance":
if humanReadable {
item[i] = fmt.Sprintf("%s%s", symbol, humanize.Commaf(entry.Balance))
} else {
item[i] = strconv.FormatFloat(entry.Balance, 'f', -1, 64)
}
case "24h%":
if humanReadable {
item[i] = fmt.Sprintf("%.2f%%", entry.PercentChange24H)
} else {
item[i] = fmt.Sprintf("%.2f", entry.PercentChange24H)
}
case "%holdings":
if humanReadable {
item[i] = fmt.Sprintf("%.2f%%", percentHoldings)
} else {
item[i] = fmt.Sprintf("%.2f", percentHoldings)
}
} }
} }
records[i] = item
} }
headers := []string{"name", "symbol", "price", "holdings", "balance", "24h%", "%holdings"}
if format == "csv" { if format == "csv" {
csvWriter := csv.NewWriter(os.Stdout) csvWriter := csv.NewWriter(os.Stdout)
if err := csvWriter.Write(headers); err != nil { if !noHeader {
return err if err := csvWriter.Write(headers); err != nil {
return err
}
} }
for _, record := range records { for _, record := range records {
@ -715,19 +769,28 @@ func (ct *Cointop) PrintHoldingsTable(options *TablePrintOptions) error {
return nil return nil
} else if format == "json" { } else if format == "json" {
list := make([]map[string]string, len(records)) var output []byte
for i, record := range records { var err error
obj := make(map[string]string, len(record)) if noHeader {
for j, column := range record { output, err = json.Marshal(records)
obj[headers[j]] = column if err != nil {
return err
} }
} else {
list := make([]map[string]string, len(records))
for i, record := range records {
obj := make(map[string]string, len(record))
for j, column := range record {
obj[headers[j]] = column
}
list[i] = obj list[i] = obj
} }
output, err := json.Marshal(list) output, err = json.Marshal(list)
if err != nil { if err != nil {
return err return err
}
} }
fmt.Println(string(output)) fmt.Println(string(output))
@ -735,9 +798,13 @@ func (ct *Cointop) PrintHoldingsTable(options *TablePrintOptions) error {
} }
alignment := []int{-1, -1, 1, 1, 1, 1, 1} alignment := []int{-1, -1, 1, 1, 1, 1, 1}
var tableHeaders []string
if !noHeader {
tableHeaders = headers
}
table := asciitable.NewAsciiTable(&asciitable.Input{ table := asciitable.NewAsciiTable(&asciitable.Input{
Data: records, Data: records,
Headers: headers, Headers: tableHeaders,
Alignment: alignment, Alignment: alignment,
}) })

@ -87,6 +87,19 @@ Ethereum ETH 394.48 100 39448 -0.18
```bash ```bash
$ cointop holdings --cols symbol,holdings,balance $ cointop holdings --cols symbol,holdings,balance
symbol holdings balance
BTC 10 118331.6
ETH 100 39490
DOGE 500000 1779.3
```
### Output without headers
```bash
$ cointop holdings --no-header
Bitcoin BTC $11,833.16 10 $118,331.6 -1.02% 74.14%
Ethereum ETH $394.9 100 $39,490 0.02% 24.74%
Dogecoin DOGE $0.00355861 500,000 $1,779.3 1.46% 1.11%
``` ```
### Convert to a different fiat currency ### Convert to a different fiat currency
@ -97,6 +110,13 @@ $ cointop holdings -h --convert eur
Ethereum ETH €278.49 100 €27,849 -15.87% 100.00% Ethereum ETH €278.49 100 €27,849 -15.87% 100.00%
``` ```
### Total portfolio value
```bash
$ cointop holdings --total
3671.32
```
### Combining flags ### Combining flags
```bash ```bash

Loading…
Cancel
Save