2
0
mirror of https://github.com/miguelmota/cointop synced 2024-11-05 00:00:14 +00:00

Add column filter option to holdings command

This commit is contained in:
Miguel Mota 2021-04-18 12:29:28 -07:00
parent 6e3b9d41f1
commit 3c97b58e09
No known key found for this signature in database
GPG Key ID: 67EC1161588A00F9
3 changed files with 132 additions and 39 deletions

View File

@ -12,12 +12,14 @@ func HoldingsCmd() *cobra.Command {
var help bool
var total bool
var noCache bool
var noHeader bool
var config string
var sortBy string
var sortDesc bool
var format string = "table"
var humanReadable bool
var filter []string
var cols []string
var convert string
holdingsCmd := &cobra.Command{
@ -52,7 +54,9 @@ func HoldingsCmd() *cobra.Command {
HumanReadable: humanReadable,
Format: format,
Filter: filter,
Cols: cols,
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(&noCache, "no-cache", "", noCache, "No cache")
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(&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().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")
return holdingsCmd

View File

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

View File

@ -87,6 +87,19 @@ Ethereum ETH 394.48 100 39448 -0.18
```bash
$ 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
@ -97,6 +110,13 @@ $ cointop holdings -h --convert eur
Ethereum ETH €278.49 100 €27,849 -15.87% 100.00%
```
### Total portfolio value
```bash
$ cointop holdings --total
3671.32
```
### Combining flags
```bash