Implement api interface for selective coin data

pull/58/head
Miguel Mota 4 years ago
parent 97facbe48d
commit 3ee33586e5

@ -988,6 +988,7 @@ Frequently asked questions:
- A: Cointop does not do any kind of mining. - A: Cointop does not do any kind of mining.
- Q: How can I run the cointop SSH server on port 22? - Q: How can I run the cointop SSH server on port 22?
- A: Port 22 is a privileged port so you need to run with `sudo`: - A: Port 22 is a privileged port so you need to run with `sudo`:
@ -996,6 +997,14 @@ Frequently asked questions:
sudo cointop server -p 22 sudo cointop server -p 22
``` ```
- Q: Why doesn't the version number work when I install with `go get`?
- A: The version number is read from the git tag during the build process but this requires the `GO111MODULE` environment variable to be set in order for Go to read the build information:
```bash
GO111MODULE=on go get github.com/miguelmota/cointop
```
## Mentioned in ## Mentioned in
Cointop has been mentioned in: Cointop has been mentioned in:

@ -19,7 +19,6 @@ func HoldingsCmd() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
ct, err := cointop.NewCointop(&cointop.Config{ ct, err := cointop.NewCointop(&cointop.Config{
ConfigFilepath: config, ConfigFilepath: config,
APIChoice: cointop.CoinGecko,
CacheDir: cointop.DefaultCacheDir, CacheDir: cointop.DefaultCacheDir,
}) })
if err != nil { if err != nil {

@ -100,7 +100,7 @@ See git.io/cointop for more info.`,
rootCmd.Flags().UintVarP(&perPage, "per-page", "", perPage, "Per page") rootCmd.Flags().UintVarP(&perPage, "per-page", "", perPage, "Per page")
rootCmd.Flags().StringVarP(&config, "config", "c", "", fmt.Sprintf("Config filepath. (default %s)", cointop.DefaultConfigFilepath)) rootCmd.Flags().StringVarP(&config, "config", "c", "", fmt.Sprintf("Config filepath. (default %s)", cointop.DefaultConfigFilepath))
rootCmd.Flags().StringVarP(&cmcAPIKey, "coinmarketcap-api-key", "", "", "Set the CoinMarketCap API key") rootCmd.Flags().StringVarP(&cmcAPIKey, "coinmarketcap-api-key", "", "", "Set the CoinMarketCap API key")
rootCmd.Flags().StringVarP(&apiChoice, "api", "", cointop.CoinGecko, "API choice. Available choices are \"coinmarketcap\" and \"coingecko\"") rootCmd.Flags().StringVarP(&apiChoice, "api", "", "", "API choice. Available choices are \"coinmarketcap\" and \"coingecko\"")
rootCmd.Flags().StringVarP(&colorscheme, "colorscheme", "", "", "Colorscheme to use (default \"cointop\"). To install standard themes, do:\n\ngit clone git@github.com:cointop-sh/colors.git ~/.config/cointop/colors\n\nSee git.io/cointop#colorschemes for more info.") rootCmd.Flags().StringVarP(&colorscheme, "colorscheme", "", "", "Colorscheme to use (default \"cointop\"). To install standard themes, do:\n\ngit clone git@github.com:cointop-sh/colors.git ~/.config/cointop/colors\n\nSee git.io/cointop#colorschemes for more info.")
rootCmd.Flags().StringVarP(&cacheDir, "cache-dir", "", cacheDir, "Cache directory") rootCmd.Flags().StringVarP(&cacheDir, "cache-dir", "", cacheDir, "Cache directory")

@ -507,13 +507,6 @@ func Reset(config *ResetConfig) error {
configDeleted := false configDeleted := false
possibleConfigPaths := []string{
"~/.config/cointop/config.toml",
"~/.config/cointop/config",
"~/.cointop/config",
"~/.cointop/config.toml",
}
for _, configPath := range possibleConfigPaths { for _, configPath := range possibleConfigPaths {
normalizedPath := pathutil.NormalizePath(configPath) normalizedPath := pathutil.NormalizePath(configPath)
if _, err := os.Stat(normalizedPath); !os.IsNotExist(err) { if _, err := os.Stat(normalizedPath); !os.IsNotExist(err) {

@ -45,9 +45,8 @@ func (s *Service) Ping() error {
return nil return nil
} }
func (s *Service) getLimitedCoinData(convert string, offset int) ([]apitypes.Coin, error) { func (s *Service) getPaginatedCoinData(convert string, offset int, names []string) ([]apitypes.Coin, error) {
var ret []apitypes.Coin var ret []apitypes.Coin
ids := []string{}
page := offset page := offset
sparkline := false sparkline := false
pcp := geckoTypes.PriceChangePercentageObject pcp := geckoTypes.PriceChangePercentageObject
@ -57,6 +56,12 @@ func (s *Service) getLimitedCoinData(convert string, offset int) ([]apitypes.Coi
if convertTo == "" { if convertTo == "" {
convertTo = "usd" convertTo = "usd"
} }
ids := make([]string, len(names))
for i, name := range names {
slug := util.NameToSlug(name)
ids[i] = slug
}
list, err := s.client.CoinsMarket(convertTo, ids, order, s.maxResultsPerPage, page, sparkline, priceChangePercentage) list, err := s.client.CoinsMarket(convertTo, ids, order, s.maxResultsPerPage, page, sparkline, priceChangePercentage)
if err != nil { if err != nil {
return nil, err return nil, err
@ -124,7 +129,7 @@ func (s *Service) GetAllCoinData(convert string, ch chan []apitypes.Coin) error
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }
coins, err := s.getLimitedCoinData(convert, i) coins, err := s.getPaginatedCoinData(convert, i, []string{})
if err != nil { if err != nil {
return return
} }
@ -135,6 +140,27 @@ func (s *Service) GetAllCoinData(convert string, ch chan []apitypes.Coin) error
return nil return nil
} }
// GetCoinData gets all data of a coin.
func (s *Service) GetCoinData(name string, convert string) (apitypes.Coin, error) {
ret := apitypes.Coin{}
ids := []string{name}
coins, err := s.getPaginatedCoinData(convert, 0, ids)
if err != nil {
return ret, err
}
if len(coins) > 0 {
ret = coins[0]
}
return ret, nil
}
// GetCoinDataBatch gets all data of specified coins.
func (s *Service) GetCoinDataBatch(names []string, convert string) ([]apitypes.Coin, error) {
return s.getPaginatedCoinData(convert, 0, names)
}
// GetCoinGraphData gets coin graph data // GetCoinGraphData gets coin graph data
func (s *Service) GetCoinGraphData(convert, symbol, name string, start, end int64) (apitypes.CoinGraph, error) { func (s *Service) GetCoinGraphData(convert, symbol, name string, start, end int64) (apitypes.CoinGraph, error) {
ret := apitypes.CoinGraph{} ret := apitypes.CoinGraph{}

@ -51,7 +51,7 @@ func (s *Service) Ping() error {
return nil return nil
} }
func (s *Service) getLimitedCoinData(convert string, offset int) ([]apitypes.Coin, error) { func (s *Service) getPaginatedCoinData(convert string, offset int) ([]apitypes.Coin, error) {
var ret []apitypes.Coin var ret []apitypes.Coin
max := 100 max := 100
@ -98,7 +98,7 @@ func (s *Service) GetAllCoinData(convert string, ch chan []apitypes.Coin) error
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }
coins, err := s.getLimitedCoinData(convert, i) coins, err := s.getPaginatedCoinData(convert, i)
if err != nil { if err != nil {
return return
} }
@ -109,6 +109,43 @@ func (s *Service) GetAllCoinData(convert string, ch chan []apitypes.Coin) error
return nil return nil
} }
// GetCoinData gets all data of a coin.
func (s *Service) GetCoinData(name string, convert string) (apitypes.Coin, error) {
ret := apitypes.Coin{}
coins, err := s.getPaginatedCoinData(convert, 0)
if err != nil {
return ret, err
}
for _, coin := range coins {
if coin.Name == name {
return coin, nil
}
}
return ret, nil
}
// GetCoinDataBatch gets all data of specified coins.
func (s *Service) GetCoinDataBatch(names []string, convert string) ([]apitypes.Coin, error) {
ret := []apitypes.Coin{}
coins, err := s.getPaginatedCoinData(convert, 0)
if err != nil {
return ret, err
}
for _, coin := range coins {
for _, name := range names {
if coin.Name == name {
ret = append(ret, coin)
break
}
}
}
return ret, nil
}
// GetCoinGraphData gets coin graph data // GetCoinGraphData gets coin graph data
func (s *Service) GetCoinGraphData(convert, symbol string, name string, start int64, end int64) (apitypes.CoinGraph, error) { func (s *Service) GetCoinGraphData(convert, symbol string, name string, start int64, end int64) (apitypes.CoinGraph, error) {
ret := apitypes.CoinGraph{} ret := apitypes.CoinGraph{}

@ -11,7 +11,8 @@ type Interface interface {
GetCoinGraphData(convert string, symbol string, name string, start int64, end int64) (types.CoinGraph, error) GetCoinGraphData(convert string, symbol string, name string, start int64, end int64) (types.CoinGraph, error)
GetGlobalMarketGraphData(convert string, start int64, end int64) (types.MarketGraph, error) GetGlobalMarketGraphData(convert string, start int64, end int64) (types.MarketGraph, error)
GetGlobalMarketData(convert string) (types.GlobalMarketData, error) GetGlobalMarketData(convert string) (types.GlobalMarketData, error)
//GetCoinData(coin string) (types.Coin, error) GetCoinData(name string, convert string) (types.Coin, error)
GetCoinDataBatch(names []string, convert string) ([]types.Coin, error)
//GetAltcoinMarketGraphData(start int64, end int64) (types.MarketGraph, error) //GetAltcoinMarketGraphData(start int64, end int64) (types.MarketGraph, error)
//GetCoinPriceUSD(coin string) (float64, error) //GetCoinPriceUSD(coin string) (float64, error)
//GetCoinMarkets(coin string) ([]types.Market, error) //GetCoinMarkets(coin string) ([]types.Market, error)

@ -15,6 +15,14 @@ import (
var fileperm = os.FileMode(0644) var fileperm = os.FileMode(0644)
// NOTE: this is to support previous default config filepaths
var possibleConfigPaths = []string{
"~/.config/cointop/config.toml",
"~/.config/cointop/config",
"~/.cointop/config",
"~/.cointop/config.toml",
}
type config struct { type config struct {
Shortcuts map[string]interface{} `toml:"shortcuts"` Shortcuts map[string]interface{} `toml:"shortcuts"`
Favorites map[string][]interface{} `toml:"favorites"` Favorites map[string][]interface{} `toml:"favorites"`
@ -42,9 +50,6 @@ func (ct *Cointop) SetupConfig() error {
if err := ct.loadFavoritesFromConfig(); err != nil { if err := ct.loadFavoritesFromConfig(); err != nil {
return err return err
} }
if err := ct.loadPortfolioFromConfig(); err != nil {
return err
}
if err := ct.loadCurrencyFromConfig(); err != nil { if err := ct.loadCurrencyFromConfig(); err != nil {
return err return err
} }
@ -63,6 +68,9 @@ func (ct *Cointop) SetupConfig() error {
if err := ct.loadRefreshRateFromConfig(); err != nil { if err := ct.loadRefreshRateFromConfig(); err != nil {
return err return err
} }
if err := ct.loadPortfolioFromConfig(); err != nil {
return err
}
return nil return nil
} }
@ -71,14 +79,8 @@ func (ct *Cointop) SetupConfig() error {
func (ct *Cointop) CreateConfigIfNotExists() error { func (ct *Cointop) CreateConfigIfNotExists() error {
ct.debuglog("createConfigIfNotExists()") ct.debuglog("createConfigIfNotExists()")
// NOTE: this is to support previous default config filepaths for _, configPath := range possibleConfigPaths {
previousDefaultConfigPaths := []string{ normalizedPath := pathutil.NormalizePath(configPath)
"~/.cointop/config",
"~/.cointop/config.toml",
}
for _, previousConfigFilepath := range previousDefaultConfigPaths {
normalizedPath := pathutil.NormalizePath(previousConfigFilepath)
if _, err := os.Stat(normalizedPath); err == nil { if _, err := os.Stat(normalizedPath); err == nil {
ct.configFilepath = normalizedPath ct.configFilepath = normalizedPath
return nil return nil
@ -218,8 +220,8 @@ func (ct *Cointop) configToToml() ([]byte, error) {
cmcIfc := map[string]interface{}{ cmcIfc := map[string]interface{}{
"pro_api_key": ct.apiKeys.cmc, "pro_api_key": ct.apiKeys.cmc,
} }
var apiChoiceIfc interface{} = ct.apiChoice
var apiChoiceIfc interface{} = ct.apiChoice
var inputs = &config{ var inputs = &config{
API: apiChoiceIfc, API: apiChoiceIfc,
Colorscheme: colorschemeIfc, Colorscheme: colorschemeIfc,

@ -184,7 +184,7 @@ func (ct *Cointop) SetPortfolioHoldings() error {
// PortfolioEntry returns a portfolio entry // PortfolioEntry returns a portfolio entry
func (ct *Cointop) PortfolioEntry(c *Coin) (*PortfolioEntry, bool) { func (ct *Cointop) PortfolioEntry(c *Coin) (*PortfolioEntry, bool) {
//ct.debuglog("portfolioEntry()") //ct.debuglog("portfolioEntry()") // too many
if c == nil { if c == nil {
return &PortfolioEntry{}, true return &PortfolioEntry{}, true
} }
@ -297,20 +297,28 @@ func (ct *Cointop) GetPortfolioTotal() float64 {
return total return total
} }
// NormalizeFloatString normalizes a float as a string // RefreshPortfolioCoins refreshes portfolio entry coin data
func normalizeFloatString(input string) string { func (ct *Cointop) RefreshPortfolioCoins() error {
re := regexp.MustCompile(`(\d+\.\d+|\.\d+|\d+)`) ct.debuglog("refreshPortfolioCoins()")
result := re.FindStringSubmatch(input) holdings := ct.GetPortfolioSlice()
if len(result) > 0 { holdingCoins := make([]string, len(holdings))
return result[0] for i, entry := range holdings {
holdingCoins[i] = entry.Name
} }
return "" coins, err := ct.api.GetCoinDataBatch(holdingCoins, ct.State.currencyConversion)
ct.processCoins(coins)
if err != nil {
return err
}
return nil
} }
// PrintHoldingsTable prints the holdings in an ASCII table // PrintHoldingsTable prints the holdings in an ASCII table
func (ct *Cointop) PrintHoldingsTable() error { func (ct *Cointop) PrintHoldingsTable() error {
ct.UpdateCoins() // fetches latest data ct.debuglog("printHoldingsTable()")
ct.RefreshPortfolioCoins()
holdings := ct.GetPortfolioSlice() holdings := ct.GetPortfolioSlice()
total := ct.GetPortfolioTotal() total := ct.GetPortfolioTotal()
data := make([][]string, len(holdings)) data := make([][]string, len(holdings))
@ -347,10 +355,22 @@ func (ct *Cointop) PrintHoldingsTable() error {
// PrintTotalHoldings prints the total holdings amount // PrintTotalHoldings prints the total holdings amount
func (ct *Cointop) PrintTotalHoldings() error { func (ct *Cointop) PrintTotalHoldings() error {
ct.UpdateCoins() // fetches latest data ct.debuglog("printTotalHoldings()")
ct.RefreshPortfolioCoins()
total := ct.GetPortfolioTotal() total := ct.GetPortfolioTotal()
symbol := ct.CurrencySymbol() symbol := ct.CurrencySymbol()
fmt.Fprintf(os.Stdout, "%s%s\n", symbol, humanize.Commaf(total)) fmt.Fprintf(os.Stdout, "%s%s\n", symbol, humanize.Commaf(total))
return nil return nil
} }
// NormalizeFloatString normalizes a float as a string
func normalizeFloatString(input string) string {
re := regexp.MustCompile(`(\d+\.\d+|\.\d+|\d+)`)
result := re.FindStringSubmatch(input)
if len(result) > 0 {
return result[0]
}
return ""
}

Loading…
Cancel
Save