More tweaking of graceful panic/recover

master
Michael Dvorkin 11 years ago
parent 7accc918cb
commit 8f55a6acf7
  1. 29
      layout.go
  2. 9
      yahoo_market.go
  3. 47
      yahoo_quotes.go

@ -53,26 +53,24 @@ func (self *Layout) Market(market *Market) string {
return err return err
} }
buffer := new(bytes.Buffer)
markup := `{{.Dow.name}}: {{.Dow.change}} ({{.Dow.percent}}) at {{.Dow.latest}}, {{.Sp500.name}}: {{.Sp500.change}} ({{.Sp500.percent}}) at {{.Sp500.latest}}, {{.Nasdaq.name}}: {{.Nasdaq.change}} ({{.Nasdaq.percent}}) at {{.Nasdaq.latest}} markup := `{{.Dow.name}}: {{.Dow.change}} ({{.Dow.percent}}) at {{.Dow.latest}}, {{.Sp500.name}}: {{.Sp500.change}} ({{.Sp500.percent}}) at {{.Sp500.latest}}, {{.Nasdaq.name}}: {{.Nasdaq.change}} ({{.Nasdaq.percent}}) at {{.Nasdaq.latest}}
{{.Advances.name}}: {{.Advances.nyse}} ({{.Advances.nysep}}) on NYSE and {{.Advances.nasdaq}} ({{.Advances.nasdaqp}}) on Nasdaq. {{.Declines.name}}: {{.Declines.nyse}} ({{.Declines.nysep}}) on NYSE and {{.Declines.nasdaq}} ({{.Declines.nasdaqp}}) on Nasdaq {{if .IsClosed}}<right>U.S. markets closed</right>{{end}} {{.Advances.name}}: {{.Advances.nyse}} ({{.Advances.nysep}}) on NYSE and {{.Advances.nasdaq}} ({{.Advances.nasdaqp}}) on Nasdaq. {{.Declines.name}}: {{.Declines.nyse}} ({{.Declines.nysep}}) on NYSE and {{.Declines.nasdaq}} ({{.Declines.nasdaqp}}) on Nasdaq {{if .IsClosed}}<right>U.S. markets closed</right>{{end}}
New highs: {{.Highs.nyse}} on NYSE and {{.Highs.nasdaq}} on Nasdaq. New lows: {{.Lows.nyse}} on NYSE and {{.Lows.nasdaq}} on Nasdaq.` New highs: {{.Highs.nyse}} on NYSE and {{.Highs.nasdaq}} on Nasdaq. New lows: {{.Lows.nyse}} on NYSE and {{.Lows.nasdaq}} on Nasdaq.`
template, err := template.New(`market`).Parse(markup)
if err != nil {
panic(err)
}
buffer := new(bytes.Buffer)
highlight(market.Dow, market.Sp500, market.Nasdaq) highlight(market.Dow, market.Sp500, market.Nasdaq)
if err := template.Execute(buffer, market); err != nil { template,_ := template.New(`market`).Parse(markup)
panic(err) template.Execute(buffer, market)
}
return buffer.String() return buffer.String()
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func (self *Layout) Quotes(quotes *Quotes) string { func (self *Layout) Quotes(quotes *Quotes) string {
if ok, err := quotes.Ok(); !ok {
return err
}
vars := struct { vars := struct {
Now string Now string
Header string Header string
@ -83,6 +81,7 @@ func (self *Layout) Quotes(quotes *Quotes) string {
self.prettify(quotes), self.prettify(quotes),
} }
buffer := new(bytes.Buffer)
markup := `<right><white>{{.Now}}</></right> markup := `<right><white>{{.Now}}</></right>
@ -90,17 +89,9 @@ func (self *Layout) Quotes(quotes *Quotes) string {
{{.Header}} {{.Header}}
{{range.Stocks}}{{if .Advancing}}<green>{{end}}{{.Ticker}}{{.LastTrade}}{{.Change}}{{.ChangePct}}{{.Open}}{{.Low}}{{.High}}{{.Low52}}{{.High52}}{{.Volume}}{{.AvgVolume}}{{.PeRatio}}{{.Dividend}}{{.Yield}}{{.MarketCap}}</> {{range.Stocks}}{{if .Advancing}}<green>{{end}}{{.Ticker}}{{.LastTrade}}{{.Change}}{{.ChangePct}}{{.Open}}{{.Low}}{{.High}}{{.Low52}}{{.High52}}{{.Volume}}{{.AvgVolume}}{{.PeRatio}}{{.Dividend}}{{.Yield}}{{.MarketCap}}</>
{{end}}` {{end}}`
//markup += fmt.Sprintf("[%v]", quotes.profile.Grouped)
template, err := template.New(`quotes`).Parse(markup)
if err != nil {
panic(err)
}
buffer := new(bytes.Buffer) template,_ := template.New(`quotes`).Parse(markup)
err = template.Execute(buffer, vars) template.Execute(buffer, vars)
if err != nil {
panic(err)
}
return buffer.String() return buffer.String()
} }

@ -1,6 +1,7 @@
// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved. // Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//-----------------------------------------------------------------------------
package mop package mop
@ -13,7 +14,7 @@ import (
`strings` `strings`
) )
const url = `http://finance.yahoo.com/marketupdate/overview` const market_url = `http://finance.yahoo.com/marketupdate/overview`
type Market struct { type Market struct {
IsClosed bool IsClosed bool
@ -67,14 +68,14 @@ func (self *Market) Initialize() *Market {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func (self *Market) Fetch() (this *Market) { func (self *Market) Fetch() (this *Market) {
this = self // <-- This ensures we return correct self in case of panic attack. this = self // <-- This ensures we return correct self after recover() from panic() attack.
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
self.errors = fmt.Sprintf("Error fetching market data...\n%s", err) self.errors = fmt.Sprintf("Error fetching market data...\n%s", err)
} }
}() }()
response, err := http.Get(url) response, err := http.Get(market_url)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -124,7 +125,7 @@ func (self *Market) trim(body []byte) []byte {
func (self *Market) extract(snippet []byte) *Market { func (self *Market) extract(snippet []byte) *Market {
matches := self.regex.FindAllStringSubmatch(string(snippet), -1) matches := self.regex.FindAllStringSubmatch(string(snippet), -1)
if len(matches) < 1 || len(matches[0]) < 37 { if len(matches) < 1 || len(matches[0]) < 37 {
panic(`Unable to parse ` + url) panic(`Unable to parse ` + market_url)
} }
self.Dow[`name`] = matches[0][1] self.Dow[`name`] = matches[0][1]

@ -1,6 +1,7 @@
// Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved. // Copyright (c) 2013 by Michael Dvorkin. All Rights Reserved.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//-----------------------------------------------------------------------------
package mop package mop
@ -35,7 +36,7 @@ import (
// j3: market cap rt // j3: market cap rt
// j1: market cap // j1: market cap
const yahoo_quotes_url = `http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=,l1c6k2oghjkva2r2rdyj3j1` const quotes_url = `http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=,l1c6k2oghjkva2r2rdyj3j1`
type Stock struct { type Stock struct {
Ticker string Ticker string
@ -62,48 +63,53 @@ type Quotes struct {
market *Market market *Market
profile *Profile profile *Profile
stocks []Stock stocks []Stock
errors string
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func (self *Quotes) Initialize(market *Market, profile *Profile) *Quotes { func (self *Quotes) Initialize(market *Market, profile *Profile) *Quotes {
self.market = market self.market = market
self.profile = profile self.profile = profile
self.errors = ``
return self return self
} }
// Fetch the latest stock quotes and parse raw fetched data into array of // Fetch the latest stock quotes and parse raw fetched data into array of
// []Stock structs. // []Stock structs.
func (self *Quotes) Fetch() *Quotes { //-----------------------------------------------------------------------------
if self.Ready() { func (self *Quotes) Fetch() (this *Quotes) {
// Format the URL and send the request. this = self // <-- This ensures we return correct self after recover() from panic() attack.
url := fmt.Sprintf(yahoo_quotes_url, self.profile.ListOfTickers()) if self.is_ready() {
defer func() {
if err := recover(); err != nil {
self.errors = fmt.Sprintf("\n\n\n\nError fetching stock quotes...\n%s", err)
}
}()
url := fmt.Sprintf(quotes_url, self.profile.ListOfTickers())
response, err := http.Get(url) response, err := http.Get(url)
if err != nil { if err != nil {
panic(err) panic(err)
} }
// Fetch response and get its body.
defer response.Body.Close() defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body) body, err := ioutil.ReadAll(response.Body)
if err != nil { if err != nil {
panic(err) panic(err)
} }
self.parse(self.sanitize(body)) self.parse(sanitize(body))
} }
return self return self
} }
// Return true if we haven't fetched the quotes yet *or* the stock market is //-----------------------------------------------------------------------------
// still open and we might want to grab the latest quotes. In both cases we func (self *Quotes) Ok() (bool, string) {
// make sure the list of requested tickers is not empty. return self.errors == ``, self.errors
func (self *Quotes) Ready() bool {
return (self.stocks == nil || !self.market.IsClosed) && len(self.profile.Tickers) > 0
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func (self *Quotes) Format() string { func (self *Quotes) Format() string {
return new(Layout).Initialize().Quotes(self) return new(Layout).Initialize().Quotes(self)
@ -125,6 +131,17 @@ func (self *Quotes) RemoveTickers(tickers []string) (removed int, err error) {
return return
} }
// "Private" methods.
// Return true if we haven't fetched the quotes yet *or* the stock market is
// still open and we might want to grab the latest quotes. In both cases we
// make sure the list of requested tickers is not empty.
//-----------------------------------------------------------------------------
func (self *Quotes) is_ready() bool {
return (self.stocks == nil || !self.market.IsClosed) && len(self.profile.Tickers) > 0
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func (self *Quotes) parse(body []byte) *Quotes { func (self *Quotes) parse(body []byte) *Quotes {
lines := bytes.Split(body, []byte{'\n'}) lines := bytes.Split(body, []byte{'\n'})
@ -155,7 +172,9 @@ func (self *Quotes) parse(body []byte) *Quotes {
return self return self
} }
// Utility methods.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func (self *Quotes) 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)
} }

Loading…
Cancel
Save