Added basics of line editor

master
Michael Dvorkin 12 years ago
parent 713b87609d
commit 8f727f1204
  1. 102
      lib/format.go
  2. 64
      lib/line_editor.go
  3. 63
      lib/profile.go
  4. 96
      lib/screen.go
  5. 236
      lib/yahoo_market.go
  6. 108
      lib/yahoo_quotes.go
  7. 23
      mop.go

@ -3,25 +3,25 @@
package mop package mop
import ( import (
"fmt"
"time"
"bytes" "bytes"
"regexp" "fmt"
"strings" "regexp"
"strings"
"text/template" "text/template"
"time"
) )
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func FormatMarket(m Market) string { func FormatMarket(m Market) string {
markup := `{{.Dow.name}}: {{.Dow.change}} ({{.Dow.percent}}) at {{.Dow.latest}}, ` markup := `{{.Dow.name}}: {{.Dow.change}} ({{.Dow.percent}}) at {{.Dow.latest}}, `
markup += `{{.Sp500.name}}: {{.Sp500.change}} ({{.Sp500.percent}}) at {{.Sp500.latest}}, ` markup += `{{.Sp500.name}}: {{.Sp500.change}} ({{.Sp500.percent}}) at {{.Sp500.latest}}, `
markup += `{{.Nasdaq.name}}: {{.Nasdaq.change}} ({{.Nasdaq.percent}}) at {{.Nasdaq.latest}}` markup += `{{.Nasdaq.name}}: {{.Nasdaq.change}} ({{.Nasdaq.percent}}) at {{.Nasdaq.latest}}`
markup += "\n" markup += "\n"
markup += `{{.Advances.name}}: {{.Advances.nyse}} ({{.Advances.nysep}}) on NYSE and {{.Advances.nasdaq}} ({{.Advances.nasdaqp}}) on Nasdaq. ` markup += `{{.Advances.name}}: {{.Advances.nyse}} ({{.Advances.nysep}}) on NYSE and {{.Advances.nasdaq}} ({{.Advances.nasdaqp}}) on Nasdaq. `
markup += `{{.Declines.name}}: {{.Declines.nyse}} ({{.Declines.nysep}}) on NYSE and {{.Declines.nasdaq}} ({{.Declines.nasdaqp}}) on Nasdaq` markup += `{{.Declines.name}}: {{.Declines.nyse}} ({{.Declines.nysep}}) on NYSE and {{.Declines.nasdaq}} ({{.Declines.nasdaqp}}) on Nasdaq`
markup += "\n" markup += "\n"
markup += `New highs: {{.Highs.nyse}} on NYSE and {{.Highs.nasdaq}} on Nasdaq. ` markup += `New highs: {{.Highs.nyse}} on NYSE and {{.Highs.nasdaq}} on Nasdaq. `
markup += `New lows: {{.Lows.nyse}} on NYSE and {{.Lows.nasdaq}} on Nasdaq.` markup += `New lows: {{.Lows.nyse}} on NYSE and {{.Lows.nasdaq}} on Nasdaq.`
template, err := template.New("market").Parse(markup) template, err := template.New("market").Parse(markup)
if err != nil { if err != nil {
panic(err) panic(err)
@ -33,18 +33,18 @@ func FormatMarket(m Market) string {
panic(err) panic(err)
} }
return buffer.String() return buffer.String()
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func FormatQuotes(quotes Quotes) string { func FormatQuotes(quotes Quotes) string {
vars := struct { vars := struct {
Now string Now string
Header string Header string
Stocks Quotes Stocks Quotes
}{ }{
time.Now().Format("3:04:05pm PST"), time.Now().Format("3:04:05pm PST"),
header(), header(),
prettify(quotes), prettify(quotes),
} }
@ -67,27 +67,27 @@ func FormatQuotes(quotes Quotes) string {
panic(err) panic(err)
} }
return buffer.String() return buffer.String()
} }
func header() string { func header() string {
str := fmt.Sprintf("%-7s ", "Ticker") str := fmt.Sprintf("%-7s ", "Ticker")
str += fmt.Sprintf("%9s ", "Last") str += fmt.Sprintf("%9s ", "Last")
str += fmt.Sprintf("%9s ", "Change") str += fmt.Sprintf("%9s ", "Change")
str += fmt.Sprintf("%9s ", "%Change") str += fmt.Sprintf("%9s ", "%Change")
str += fmt.Sprintf("%9s ", "Open") str += fmt.Sprintf("%9s ", "Open")
str += fmt.Sprintf("%9s ", "Low") str += fmt.Sprintf("%9s ", "Low")
str += fmt.Sprintf("%9s ", "High") str += fmt.Sprintf("%9s ", "High")
str += fmt.Sprintf("%9s ", "52w Low") str += fmt.Sprintf("%9s ", "52w Low")
str += fmt.Sprintf("%9s ", "52w High") str += fmt.Sprintf("%9s ", "52w High")
str += fmt.Sprintf("%10s ", "Volume") str += fmt.Sprintf("%10s ", "Volume")
str += fmt.Sprintf("%10s ", "AvgVolume") str += fmt.Sprintf("%10s ", "AvgVolume")
str += fmt.Sprintf("%9s ", "P/E") str += fmt.Sprintf("%9s ", "P/E")
str += fmt.Sprintf("%9s ", "Dividend") str += fmt.Sprintf("%9s ", "Dividend")
str += fmt.Sprintf("%9s ", "Yield") str += fmt.Sprintf("%9s ", "Yield")
str += fmt.Sprintf("%10s", "MktCap") str += fmt.Sprintf("%10s", "MktCap")
return str return str
} }
func prettify(quotes Quotes) Quotes { func prettify(quotes Quotes) Quotes {
@ -113,19 +113,19 @@ func prettify(quotes Quotes) Quotes {
} }
func nullify(str string) string { func nullify(str string) string {
if len(str) == 3 && str[0:3] == "N/A" { if len(str) == 3 && str[0:3] == "N/A" {
return "-" return "-"
} else { } else {
return str return str
} }
} }
func last_of_pair(str string) string { func last_of_pair(str string) string {
if len(str) >= 6 && str[0:6] != "N/A - " { if len(str) >= 6 && str[0:6] != "N/A - " {
return str return str
} else { } else {
return str[6:] return str[6:]
} }
} }
func with_currency(str string) string { func with_currency(str string) string {
@ -168,14 +168,16 @@ func ticker(str string, change string) string {
} }
func pad(str string, width int) string { func pad(str string, width int) string {
re := regexp.MustCompile(`(\.\d+)[MB]?$`) re := regexp.MustCompile(`(\.\d+)[MB]?$`)
match := re.FindStringSubmatch(str) match := re.FindStringSubmatch(str)
if len(match) > 0 { if len(match) > 0 {
switch len(match[1]) { switch len(match[1]) {
case 2: str = strings.Replace(str, match[1], match[1] + "0", 1) case 2:
case 4, 5: str = strings.Replace(str, match[1], match[1][0:3], 1) str = strings.Replace(str, match[1], match[1]+"0", 1)
} case 4, 5:
} str = strings.Replace(str, match[1], match[1][0:3], 1)
}
}
return fmt.Sprintf("%*s", width, str) return fmt.Sprintf("%*s", width, str)
} }

@ -0,0 +1,64 @@
// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
package mop
import (
"github.com/nsf/termbox-go"
)
// const (
// add_prompt = "Add tickers: "
// remove_prompt = "Remove tickers: "
// )
// const prompts = map[rune]string{'+': `Add tickers: `, '-': `Remove tickers: `}
type LineEditor struct {
command rune
prompt string
cursor int
input string
}
//-----------------------------------------------------------------------------
func (self *LineEditor) Prompt(command rune) {
prompts := map[rune]string{'+': `Add tickers: `, '-': `Remove tickers: `}
self.command = command
switch self.command {
case '+', '-':
self.prompt = prompts[self.command]
// if self.command == '+' {
// self.prompt = add_prompt
// } else {
// self.prompt = remove_prompt
// }
DrawLine(0, 3, "<white>"+self.prompt+"</white>")
termbox.SetCursor(len(self.prompt), 3)
termbox.Flush()
}
}
//-----------------------------------------------------------------------------
func (self *LineEditor) Handle(ev termbox.Event) bool {
switch ev.Key {
case termbox.KeyEsc:
ClearLine(0, 3)
termbox.HideCursor()
termbox.Flush()
return true
case termbox.KeyEnter:
ClearLine(0, 3)
termbox.HideCursor()
termbox.Flush()
return true
default:
if ev.Ch != 0 {
self.input += string(ev.Ch)
DrawLine(len(self.prompt), 3, self.input)
termbox.SetCursor(len(self.prompt)+len(self.input), 3)
termbox.Flush()
}
}
return false
}

@ -1,56 +1,57 @@
// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved. // Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
package mop package mop
import ( import (
"strings" "encoding/json"
"os/user" "io/ioutil"
"io/ioutil" "os/user"
"encoding/json" "strings"
) )
const rcfile = "/.moprc" const rcfile = "/.moprc"
type Profile struct { type Profile struct {
MarketRefreshRate int MarketRefreshRate int
QuotesRefreshRate int QuotesRefreshRate int
Tickers []string Tickers []string
SortBy string SortBy string
SortOrder string SortOrder string
} }
var profile Profile var profile Profile
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func LoadProfile() string { func LoadProfile() string {
data, err := ioutil.ReadFile(defaultProfile()) data, err := ioutil.ReadFile(defaultProfile())
if err != nil { if err != nil {
// Set default values. // Set default values.
profile.MarketRefreshRate = 12 profile.MarketRefreshRate = 12
profile.QuotesRefreshRate = 5 profile.QuotesRefreshRate = 5
profile.Tickers = []string{ "AAPL", "C", "GOOG", "IBM", "KO", "ORCL", "V" } profile.Tickers = []string{"AAPL", "C", "GOOG", "IBM", "KO", "ORCL", "V"}
profile.SortBy = "Ticker" profile.SortBy = "Ticker"
profile.SortOrder = "Desc" profile.SortOrder = "Desc"
profile.Save() profile.Save()
} else { } else {
json.Unmarshal(data, &profile) json.Unmarshal(data, &profile)
} }
return strings.Join(profile.Tickers, "+") return strings.Join(profile.Tickers, "+")
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func (profile *Profile) Save() error { func (profile *Profile) Save() error {
if data, err := json.Marshal(profile); err != nil { if data, err := json.Marshal(profile); err != nil {
return err return err
} else { } else {
return ioutil.WriteFile(defaultProfile(), data, 0644) return ioutil.WriteFile(defaultProfile(), data, 0644)
} }
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func defaultProfile() string { func defaultProfile() string {
usr, err := user.Current() usr, err := user.Current()
if err != nil { if err != nil {
panic(err) panic(err)
} }
return usr.HomeDir + rcfile return usr.HomeDir + rcfile
} }

@ -5,9 +5,9 @@ package mop
import ( import (
"github.com/michaeldv/just" "github.com/michaeldv/just"
"github.com/nsf/termbox-go" "github.com/nsf/termbox-go"
"time"
"regexp" "regexp"
"strings" "strings"
"time"
) )
// Can combine attributes and a single color using bitwise OR. // Can combine attributes and a single color using bitwise OR.
@ -31,71 +31,31 @@ var tags = map[string]termbox.Attribute{
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func DrawMarket() { func DrawMarket() {
market := GetMarket() market := GetMarket()
// for _, m := range message {
// fmt.Printf("%s, %s, %s\n", m.Ticker, m.LastTrade, m.Change)
// }
// fmt.Printf("%s\n", Format(message))
drawScreen(FormatMarket(market)) drawScreen(FormatMarket(market))
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func DrawQuotes(stocks string) { func DrawQuotes(stocks string) {
quotes := GetQuotes(stocks) quotes := GetQuotes(stocks)
// for _, m := range message {
// fmt.Printf("%s, %s, %s\n", m.Ticker, m.LastTrade, m.Change)
// }
// fmt.Printf("%s\n", Format(message))
drawScreen(FormatQuotes(quotes)) drawScreen(FormatQuotes(quotes))
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func DrawTime() { func DrawTime() {
now := time.Now().Format("3:04:05pm PST") now := time.Now().Format("3:04:05pm PST")
drawLine(0, 0, "<right>" + now + "</right>") DrawLine(0, 0, "<right>"+now+"</right>")
}
//
// Return regular expression that matches all possible tags, i.e.
// </?black>|</?red>| ... |</?white>
//-----------------------------------------------------------------------------
func tagsRegexp() *regexp.Regexp {
arr := []string{}
for tag, _ := range tags {
arr = append(arr, "</?"+tag+">")
}
return regexp.MustCompile(strings.Join(arr, "|"))
} }
//
// Return true if a string looks like a tag.
//-----------------------------------------------------------------------------
func isTag(str string) (is bool, open bool) {
is = (len(str) > 3 && str[0:1] == "<" && str[len(str)-1:] == ">")
open = (is && str[1:2] != "/")
return
}
//
// Extract tag name from the given tag, i.e. "<hello>" => "hello"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func tagName(str string) string { func ClearLine(x int, y int) {
if len(str) < 3 { width, _ := termbox.Size()
return "" for i := x; i < width; i++ {
} else if str[1:2] != "/" { termbox.SetCell(i, y, ' ', termbox.ColorDefault, termbox.ColorDefault)
return str[1 : len(str)-1]
} else {
return str[2 : len(str)-1]
} }
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func drawLine(x int, y int, str string) { func DrawLine(x int, y int, str string) {
column, right := 0, false column, right := 0, false
foreground, background := termbox.ColorDefault, termbox.ColorDefault foreground, background := termbox.ColorDefault, termbox.ColorDefault
@ -127,12 +87,48 @@ func drawLine(x int, y int, str string) {
column += 1 column += 1
} }
} }
termbox.Flush() termbox.Flush()
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func drawScreen(str string) { func drawScreen(str string) {
for row, line := range strings.Split(str, "\n") { for row, line := range strings.Split(str, "\n") {
drawLine(0, row, line) DrawLine(0, row, line)
}
}
//
// Return regular expression that matches all possible tags, i.e.
// </?black>|</?red>| ... |</?white>
//-----------------------------------------------------------------------------
func tagsRegexp() *regexp.Regexp {
arr := []string{}
for tag, _ := range tags {
arr = append(arr, "</?"+tag+">")
}
return regexp.MustCompile(strings.Join(arr, "|"))
}
//
// Return true if a string looks like a tag.
//-----------------------------------------------------------------------------
func isTag(str string) (is bool, open bool) {
is = (len(str) > 3 && str[0:1] == "<" && str[len(str)-1:] == ">")
open = (is && str[1:2] != "/")
return
}
//
// Extract tag name from the given tag, i.e. "<hello>" => "hello"
//-----------------------------------------------------------------------------
func tagName(str string) string {
if len(str) < 3 {
return ""
} else if str[1:2] != "/" {
return str[1 : len(str)-1]
} else {
return str[2 : len(str)-1]
} }
} }

@ -3,23 +3,22 @@
package mop package mop
import ( import (
//"fmt" "bytes"
"bytes" "io/ioutil"
"regexp" "net/http"
"strings" "regexp"
"net/http" "strings"
"io/ioutil"
) )
type Market struct { type Market struct {
Dow map[string]string Dow map[string]string
Nasdaq map[string]string Nasdaq map[string]string
Sp500 map[string]string Sp500 map[string]string
Advances map[string]string Advances map[string]string
Declines map[string]string Declines map[string]string
Unchanged map[string]string Unchanged map[string]string
Highs map[string]string Highs map[string]string
Lows map[string]string Lows map[string]string
} }
const yahoo_market_url = `http://finance.yahoo.com/marketupdate/overview` const yahoo_market_url = `http://finance.yahoo.com/marketupdate/overview`
@ -37,114 +36,115 @@ func GetMarket() Market {
panic(err) panic(err)
} }
return extract(trim(body)) return extract(trim(body))
} }
func trim(body []byte) []byte { func trim(body []byte) []byte {
start := bytes.Index(body, []byte("<table id=\"yfimktsumm\"")) start := bytes.Index(body, []byte("<table id=\"yfimktsumm\""))
finish := bytes.LastIndex(body, []byte("<table id=\"yfimktsumm\"")) finish := bytes.LastIndex(body, []byte("<table id=\"yfimktsumm\""))
snippet := bytes.Replace(body[start:finish], []byte{'\n'}, []byte{}, -1) snippet := bytes.Replace(body[start:finish], []byte{'\n'}, []byte{}, -1)
snippet = bytes.Replace(snippet, []byte("&amp;"), []byte{'&'}, -1) snippet = bytes.Replace(snippet, []byte("&amp;"), []byte{'&'}, -1)
return snippet return snippet
} }
func extract(snippet []byte) Market { func extract(snippet []byte) Market {
const any = `\s*<.+?>` const any = `\s*<.+?>`
const some = `<.+?` const some = `<.+?`
const space = `\s*` const space = `\s*`
const arrow = `"(Up|Down)">\s*` const arrow = `"(Up|Down)">\s*`
const price = `([\d\.,]+)` const price = `([\d\.,]+)`
const percent = `\(([\d\.,%]+)\)` const percent = `\(([\d\.,%]+)\)`
regex := []string{ regex := []string{
"(Dow)", any, price, some, arrow, any, price, some, percent, any, "(Dow)", any, price, some, arrow, any, price, some, percent, any,
"(Nasdaq)", any, price, some, arrow, any, price, some, percent, any, "(Nasdaq)", any, price, /*some, arrow,*/ any, price, some, percent, any,
"(S&P 500)", any, price, some, arrow, any, price, some, percent, any, "(S&P 500)", any, price, some, arrow, any, price, some, percent, any,
"(Advances)", any, price, space, percent, any, price, space, percent, any, "(Advances)", any, price, space, percent, any, price, space, percent, any,
"(Declines)", any, price, space, percent, any, price, space, percent, any, "(Declines)", any, price, space, percent, any, price, space, percent, any,
"(Unchanged)", any, price, space, percent, any, price, space, percent, any, "(Unchanged)", any, price, space, percent, any, price, space, percent, any,
"(New Hi's)", any, price, any, price, any, "(New Hi's)", any, price, any, price, any,
"(New Lo's)", any, price, any, price, any, "(New Lo's)", any, price, any, price, any,
} }
re := regexp.MustCompile(strings.Join(regex, "")) re := regexp.MustCompile(strings.Join(regex, ""))
matches := re.FindAllStringSubmatch(string(snippet), -1) matches := re.FindAllStringSubmatch(string(snippet), -1)
// if len(matches) > 0 { // if len(matches) > 0 {
// fmt.Printf("%d matches\n", len(matches[0])) // fmt.Printf("%d matches\n", len(matches[0]))
// for i, str := range matches[0][1:] { // for i, str := range matches[0][1:] {
// fmt.Printf("%d) [%s]\n", i, str) // fmt.Printf("%d) [%s]\n", i, str)
// } // }
// } else { // } else {
// println("No matches") // println("No matches")
// } // }
m := Market{ m := Market{
Dow: make(map[string]string), Dow: make(map[string]string),
Nasdaq: make(map[string]string), Nasdaq: make(map[string]string),
Sp500: make(map[string]string), Sp500: make(map[string]string),
Advances: make(map[string]string), Advances: make(map[string]string),
Declines: make(map[string]string), Declines: make(map[string]string),
Unchanged: make(map[string]string), Unchanged: make(map[string]string),
Highs: make(map[string]string), Highs: make(map[string]string),
Lows: make(map[string]string), Lows: make(map[string]string),
} }
m.Dow[`name`] = matches[0][1] return m
m.Dow[`latest`] = matches[0][2] m.Dow[`name`] = matches[0][1]
m.Dow[`change`] = matches[0][4] m.Dow[`latest`] = matches[0][2]
if matches[0][3] == "Up" { m.Dow[`change`] = matches[0][4]
m.Dow[`change`] = "+" + matches[0][4] if matches[0][3] == "Up" {
m.Dow[`percent`] = "+" + matches[0][5] m.Dow[`change`] = "+" + matches[0][4]
} else { m.Dow[`percent`] = "+" + matches[0][5]
m.Dow[`change`] = "-" + matches[0][4] } else {
m.Dow[`percent`] = "-" + matches[0][5] m.Dow[`change`] = "-" + matches[0][4]
} m.Dow[`percent`] = "-" + matches[0][5]
}
m.Nasdaq[`name`] = matches[0][6]
m.Nasdaq[`latest`] = matches[0][7] m.Nasdaq[`name`] = matches[0][6]
if matches[0][8] == "Up" { m.Nasdaq[`latest`] = matches[0][7]
m.Nasdaq[`change`] = "+" + matches[0][9] if matches[0][8] == "Up" {
m.Nasdaq[`percent`] = "+" + matches[0][10] m.Nasdaq[`change`] = "+" + matches[0][9]
} else { m.Nasdaq[`percent`] = "+" + matches[0][10]
m.Nasdaq[`change`] = "-" + matches[0][9] } else {
m.Nasdaq[`percent`] = "-" + matches[0][10] m.Nasdaq[`change`] = "-" + matches[0][9]
} m.Nasdaq[`percent`] = "-" + matches[0][10]
}
m.Sp500[`name`] = matches[0][11]
m.Sp500[`latest`] = matches[0][12] m.Sp500[`name`] = matches[0][11]
if matches[0][13] == "Up" { m.Sp500[`latest`] = matches[0][12]
m.Sp500[`change`] = "+" + matches[0][14] if matches[0][13] == "Up" {
m.Sp500[`percent`] = "+" + matches[0][15] m.Sp500[`change`] = "+" + matches[0][14]
} else { m.Sp500[`percent`] = "+" + matches[0][15]
m.Sp500[`change`] = "-" + matches[0][14] } else {
m.Sp500[`percent`] = "-" + matches[0][15] m.Sp500[`change`] = "-" + matches[0][14]
} m.Sp500[`percent`] = "-" + matches[0][15]
}
m.Advances[`name`] = matches[0][16]
m.Advances[`nyse`] = matches[0][17] m.Advances[`name`] = matches[0][16]
m.Advances[`nysep`] = matches[0][18] m.Advances[`nyse`] = matches[0][17]
m.Advances[`nasdaq`] = matches[0][19] m.Advances[`nysep`] = matches[0][18]
m.Advances[`nasdaqp`] = matches[0][20] m.Advances[`nasdaq`] = matches[0][19]
m.Advances[`nasdaqp`] = matches[0][20]
m.Declines[`name`] = matches[0][21]
m.Declines[`nyse`] = matches[0][22] m.Declines[`name`] = matches[0][21]
m.Declines[`nysep`] = matches[0][23] m.Declines[`nyse`] = matches[0][22]
m.Declines[`nasdaq`] = matches[0][24] m.Declines[`nysep`] = matches[0][23]
m.Declines[`nasdaqp`] = matches[0][25] m.Declines[`nasdaq`] = matches[0][24]
m.Declines[`nasdaqp`] = matches[0][25]
m.Unchanged[`name`] = matches[0][26]
m.Unchanged[`nyse`] = matches[0][27] m.Unchanged[`name`] = matches[0][26]
m.Unchanged[`nysep`] = matches[0][28] m.Unchanged[`nyse`] = matches[0][27]
m.Unchanged[`nasdaq`] = matches[0][29] m.Unchanged[`nysep`] = matches[0][28]
m.Unchanged[`nasdaqp`] = matches[0][30] m.Unchanged[`nasdaq`] = matches[0][29]
m.Unchanged[`nasdaqp`] = matches[0][30]
m.Highs[`name`] = matches[0][31]
m.Highs[`nyse`] = matches[0][32] m.Highs[`name`] = matches[0][31]
m.Highs[`nasdaq`] = matches[0][33] m.Highs[`nyse`] = matches[0][32]
m.Lows[`name`] = matches[0][34] m.Highs[`nasdaq`] = matches[0][33]
m.Lows[`nyse`] = matches[0][35] m.Lows[`name`] = matches[0][34]
m.Lows[`nasdaq`] = matches[0][36] m.Lows[`nyse`] = matches[0][35]
m.Lows[`nasdaq`] = matches[0][36]
return m;
return m
} }

@ -3,11 +3,11 @@
package mop package mop
import ( import (
"fmt" "bytes"
"bytes" "fmt"
"strings" "io/ioutil"
"net/http" "net/http"
"io/ioutil" "strings"
) )
// See http://www.gummy-stuff.org/Yahoo-data.htm // See http://www.gummy-stuff.org/Yahoo-data.htm
@ -37,31 +37,31 @@ const yahoo_quotes_url = `http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=
// "TSLA", 120.09, "+4.85", "N/A - +4.21%", 118.75, 115.70, 120.28, 25.52, 121.89, 6827497, 9464530, N/A, N/A, 0.00, N/A, N/A, 13.877B // "TSLA", 120.09, "+4.85", "N/A - +4.21%", 118.75, 115.70, 120.28, 25.52, 121.89, 6827497, 9464530, N/A, N/A, 0.00, N/A, N/A, 13.877B
type Quote struct { type Quote struct {
Ticker string Ticker string
LastTrade string LastTrade string
Change string Change string
ChangePercent string ChangePercent string
Open string Open string
Low string Low string
High string High string
Low52 string Low52 string
High52 string High52 string
Volume string Volume string
AvgVolume string AvgVolume string
PeRatio string PeRatio string
PeRatioX string PeRatioX string
Dividend string Dividend string
Yield string Yield string
MarketCap string MarketCap string
MarketCapX string MarketCapX string
} }
type Quotes []Quote type Quotes []Quote
func GetQuotes(tickers string) Quotes { func GetQuotes(tickers string) Quotes {
// Format the URL and send the request. // Format the URL and send the request.
// url := fmt.Sprintf(yahoo_quotes_url, strings.Join(tickers, "+")) // url := fmt.Sprintf(yahoo_quotes_url, strings.Join(tickers, "+"))
url := fmt.Sprintf(yahoo_quotes_url, tickers) url := fmt.Sprintf(yahoo_quotes_url, tickers)
response, err := http.Get(url) response, err := http.Get(url)
if err != nil { if err != nil {
panic(err) panic(err)
@ -74,7 +74,7 @@ func GetQuotes(tickers string) Quotes {
panic(err) panic(err)
} }
return parse(sanitize(body)) return parse(sanitize(body))
} }
func (q *Quote) Color() string { func (q *Quote) Color() string {
@ -86,46 +86,46 @@ func (q *Quote) Color() string {
} }
func sanitize(body []byte) []byte { func sanitize(body []byte) []byte {
return bytes.Replace(bytes.TrimSpace(body), []byte{'"'}, []byte{}, -1) return bytes.Replace(bytes.TrimSpace(body), []byte{'"'}, []byte{}, -1)
} }
func parse(body []byte) Quotes { func parse(body []byte) Quotes {
lines := bytes.Split(body, []byte{'\n'}) lines := bytes.Split(body, []byte{'\n'})
quotes := make(Quotes, len(lines)) quotes := make(Quotes, len(lines))
for i,line := range lines { for i, line := range lines {
// fmt.Printf("\n\n{%d} -> [%s]\n\n", i, string(line)) // fmt.Printf("\n\n{%d} -> [%s]\n\n", i, string(line))
parse_line(line, &quotes[i]) parse_line(line, &quotes[i])
} }
return quotes return quotes
} }
func parse_line(line []byte, quote *Quote) { func parse_line(line []byte, quote *Quote) {
columns := bytes.Split(bytes.TrimSpace(line), []byte{','}) columns := bytes.Split(bytes.TrimSpace(line), []byte{','})
quote.Ticker = string(columns[0]) quote.Ticker = string(columns[0])
quote.LastTrade = string(columns[1]) quote.LastTrade = string(columns[1])
quote.Change = string(columns[2]) quote.Change = string(columns[2])
quote.ChangePercent = string(columns[3]) quote.ChangePercent = string(columns[3])
quote.Open = string(columns[4]) quote.Open = string(columns[4])
quote.Low = string(columns[5]) quote.Low = string(columns[5])
quote.High = string(columns[6]) quote.High = string(columns[6])
quote.Low52 = string(columns[7]) quote.Low52 = string(columns[7])
quote.High52 = string(columns[8]) quote.High52 = string(columns[8])
quote.Volume = string(columns[9]) quote.Volume = string(columns[9])
quote.AvgVolume = string(columns[10]) quote.AvgVolume = string(columns[10])
quote.PeRatio = string(columns[11]) quote.PeRatio = string(columns[11])
quote.PeRatioX = string(columns[12]) quote.PeRatioX = string(columns[12])
quote.Dividend = string(columns[13]) quote.Dividend = string(columns[13])
quote.Yield = string(columns[14]) quote.Yield = string(columns[14])
quote.MarketCap = string(columns[15]) quote.MarketCap = string(columns[15])
quote.MarketCapX = string(columns[16]) quote.MarketCapX = string(columns[16])
} }
// func (quotes Quotes) Format() string { // func (quotes Quotes) Format() string {
// str := time.Now().Format("3:04:05pm PST\n") // str := time.Now().Format("3:04:05pm PST\n")
// //
// for _, q := range quotes { // for _, q := range quotes {
// str += fmt.Sprintf("%s - %s - %s - %s\n", q.Ticker, q.Ask, q.Change, q.ChangePercent) // str += fmt.Sprintf("%s - %s - %s - %s\n", q.Ticker, q.Ask, q.Change, q.ChangePercent)
// } // }
@ -319,4 +319,4 @@ func parse_line(line []byte, quote *Quote) {
// ] // ]
// } // }
// } // }
// } // }

@ -18,6 +18,7 @@ func initTermbox() {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func mainLoop(profile string) { func mainLoop(profile string) {
var line_editor *mop.LineEditor
keyboard_queue := make(chan termbox.Event) keyboard_queue := make(chan termbox.Event)
timestamp_queue := time.NewTicker(1 * time.Second) timestamp_queue := time.NewTicker(1 * time.Second)
quotes_queue := time.NewTicker(5 * time.Second) quotes_queue := time.NewTicker(5 * time.Second)
@ -29,7 +30,7 @@ func mainLoop(profile string) {
} }
}() }()
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
mop.DrawMarket() mop.DrawMarket()
mop.DrawQuotes(profile) mop.DrawQuotes(profile)
loop: loop:
@ -38,13 +39,23 @@ loop:
case event := <-keyboard_queue: case event := <-keyboard_queue:
switch event.Type { switch event.Type {
case termbox.EventKey: case termbox.EventKey:
if event.Key == termbox.KeyEsc { if line_editor == nil {
break loop if event.Key == termbox.KeyEsc {
break loop
} else if event.Ch == '+' || event.Ch == '-' {
line_editor = new(mop.LineEditor)
line_editor.Prompt(event.Ch)
}
} else {
done := line_editor.Handle(event)
if done {
line_editor = nil
}
} }
case termbox.EventResize: case termbox.EventResize:
termbox.Clear(termbox.ColorDefault, termbox.ColorDefault) termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
mop.DrawMarket() mop.DrawMarket()
mop.DrawQuotes(profile) mop.DrawQuotes(profile)
} }
case <-timestamp_queue.C: case <-timestamp_queue.C:

Loading…
Cancel
Save