From 832284dc263dad8b0bb685ed25382e13222ed672 Mon Sep 17 00:00:00 2001 From: Miguel Mota Date: Mon, 10 Aug 2020 20:34:15 -0700 Subject: [PATCH] Add sort options for holdings command --- cointop/cmd/holdings.go | 29 ++++++++++++--- cointop/portfolio.go | 78 ++++++++++++++++++++++++++++++++++------- 2 files changed, 89 insertions(+), 18 deletions(-) diff --git a/cointop/cmd/holdings.go b/cointop/cmd/holdings.go index 2199d72..fba2c3f 100644 --- a/cointop/cmd/holdings.go +++ b/cointop/cmd/holdings.go @@ -9,14 +9,23 @@ import ( // HoldingsCmd ... func HoldingsCmd() *cobra.Command { - var total, noCache bool + var help bool + var total bool + var noCache bool var config string + var sortBy string + var sortDesc bool + var humanReadable bool holdingsCmd := &cobra.Command{ Use: "holdings", Short: "Displays current holdings", Long: `The holdings command shows your current holdings`, RunE: func(cmd *cobra.Command, args []string) error { + if help { + return cmd.Help() + } + ct, err := cointop.NewCointop(&cointop.Config{ ConfigFilepath: config, CacheDir: cointop.DefaultCacheDir, @@ -26,16 +35,26 @@ func HoldingsCmd() *cobra.Command { } if total { - return ct.PrintTotalHoldings() + return ct.PrintTotalHoldings(&cointop.TablePrintOptions{ + HumanReadable: humanReadable, + }) } - return ct.PrintHoldingsTable() + return ct.PrintHoldingsTable(&cointop.TablePrintOptions{ + SortBy: sortBy, + SortDesc: sortDesc, + HumanReadable: humanReadable, + }) }, } - holdingsCmd.Flags().BoolVarP(&total, "total", "t", false, "Show total only") - holdingsCmd.Flags().BoolVarP(&noCache, "no-cache", "", false, "No cache") + holdingsCmd.Flags().BoolVarP(&help, "help", "", help, "Help for holdings") + 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().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") return holdingsCmd } diff --git a/cointop/portfolio.go b/cointop/portfolio.go index 1eec448..659dc92 100644 --- a/cointop/portfolio.go +++ b/cointop/portfolio.go @@ -315,11 +315,42 @@ func (ct *Cointop) RefreshPortfolioCoins() error { return nil } +// TablePrintOptions are options for ascii table output. +type TablePrintOptions struct { + SortBy string + SortDesc bool + HumanReadable bool +} + +// portfolioColumns is list of column keys for portfolio +var portfolioColumns = map[string]bool{ + "name": true, + "symbol": true, + "price": true, + "holdings": true, + "balance": true, + "24h": true, +} + // PrintHoldingsTable prints the holdings in an ASCII table -func (ct *Cointop) PrintHoldingsTable() error { +func (ct *Cointop) PrintHoldingsTable(options *TablePrintOptions) error { ct.debuglog("printHoldingsTable()") ct.RefreshPortfolioCoins() + + if options == nil { + options = &TablePrintOptions{} + } + holdings := ct.GetPortfolioSlice() + + if options.SortBy != "" { + if _, ok := portfolioColumns[options.SortBy]; !ok { + return fmt.Errorf("The option %q is not a valid column name", options.SortBy) + } + + ct.Sort(options.SortBy, options.SortDesc, holdings, true) + } + total := ct.GetPortfolioTotal() data := make([][]string, len(holdings)) symbol := ct.CurrencySymbol() @@ -330,19 +361,31 @@ func (ct *Cointop) PrintHoldingsTable() error { percentHoldings = 0 } - data = append(data, []string{ - entry.Name, - entry.Symbol, - humanize.Commaf(entry.Price), - humanize.Commaf(entry.Holdings), - humanize.Commaf(entry.Balance), - fmt.Sprintf("%.2f%%", entry.PercentChange24H), - fmt.Sprintf("%.2f%%", percentHoldings), - }) + if options.HumanReadable { + data = append(data, []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 { + data = append(data, []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), + }) + } } alignment := []int{-1, -1, 1, 1, 1, 1, 1} - headers := []string{"name", "symbol", fmt.Sprintf("%sprice", symbol), "holdings", fmt.Sprintf("%sbalance", symbol), "24h%", "%holdings"} + headers := []string{"name", "symbol", "price", "holdings", "balance", "24h%", "%holdings"} table := asciitable.NewAsciiTable(&asciitable.Input{ Data: data, Headers: headers, @@ -354,13 +397,22 @@ func (ct *Cointop) PrintHoldingsTable() error { } // PrintTotalHoldings prints the total holdings amount -func (ct *Cointop) PrintTotalHoldings() error { +func (ct *Cointop) PrintTotalHoldings(options *TablePrintOptions) error { ct.debuglog("printTotalHoldings()") + if options == nil { + options = &TablePrintOptions{} + } + ct.RefreshPortfolioCoins() total := ct.GetPortfolioTotal() symbol := ct.CurrencySymbol() - fmt.Fprintf(os.Stdout, "%s%s\n", symbol, humanize.Commaf(total)) + if options.HumanReadable { + fmt.Fprintf(os.Stdout, "%s%s\n", symbol, humanize.Commaf(total)) + } else { + fmt.Fprintf(os.Stdout, "%s\n", strconv.FormatFloat(total, 'f', -1, 64)) + } + return nil }