From b0ecc94ef69d489dcc775038a0c6f65c1c960c8e Mon Sep 17 00:00:00 2001 From: bnewbold Date: Thu, 20 Sep 2012 15:36:30 +0200 Subject: WIP --- octopart.go | 277 ++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 155 insertions(+), 122 deletions(-) (limited to 'octopart.go') diff --git a/octopart.go b/octopart.go index 367bc99..654c03d 100644 --- a/octopart.go +++ b/octopart.go @@ -1,12 +1,12 @@ package main import ( - "net/http" - "net/url" - "encoding/json" - "bytes" - "log" - //"io/ioutil" + "bytes" + "encoding/json" + "log" + "net/http" + "net/url" + //"io/ioutil" ) /* @@ -17,143 +17,176 @@ LineItems. var pricingSource *OctopartClient type OctopartClient struct { - ApiKey string - RemoteHost string - client *http.Client - infoCache map[string]interface{} + ApiKey string + RemoteHost string + client *http.Client + infoCache map[string]interface{} } func NewOctopartClient(apikey string) *OctopartClient { - oc := &OctopartClient{ApiKey: apikey, - RemoteHost: "https://octopart.com"} - oc.client = &http.Client{} - oc.infoCache = make(map[string]interface{}) - return oc + oc := &OctopartClient{ApiKey: apikey, + RemoteHost: "https://octopart.com"} + oc.client = &http.Client{} + oc.infoCache = make(map[string]interface{}) + return oc } func openPricingSource() { - // TODO: pass through octopart API key here - pricingSource = NewOctopartClient("") + // TODO: pass through octopart API key here + pricingSource = NewOctopartClient("") } func (oc *OctopartClient) apiCall(method string, params map[string]string) (map[string]interface{}, error) { - paramString := "?apikey=" + oc.ApiKey - // TODO: assert clean-ness of params - // TODO: use url.Values instead... - for key := range params { - paramString += "&" + url.QueryEscape(key) + "=" + url.QueryEscape(params[key]) - } - paramStringUnescaped, _ := url.QueryUnescape(paramString) // TODO: err - log.Println("Fetching: " + oc.RemoteHost + "/api/v2/" + method + paramStringUnescaped) - resp, err := oc.client.Get(oc.RemoteHost + "/api/v2/" + method + paramString) - if err != nil { - return nil, err - } - if resp.StatusCode != 200 { - return nil, Error("Octopart API call error: " + resp.Status) - } - result := make(map[string]interface{}) - defer resp.Body.Close() - //body, err := ioutil.ReadAll(resp.Body) - //if err != nil { - // return nil, err - //} - //body = append(body, '\n') - //dec := json.NewDecoder(bytes.NewReader(body)) - dec := json.NewDecoder(resp.Body) - if err = dec.Decode(&result); err != nil { - return nil, err - } - return result, nil + paramString := "?apikey=" + oc.ApiKey + // TODO: assert clean-ness of params + // TODO: use url.Values instead... + for key := range params { + paramString += "&" + url.QueryEscape(key) + "=" + url.QueryEscape(params[key]) + } + paramStringUnescaped, _ := url.QueryUnescape(paramString) // TODO: err + log.Println("Fetching: " + oc.RemoteHost + "/api/v2/" + method + paramStringUnescaped) + resp, err := oc.client.Get(oc.RemoteHost + "/api/v2/" + method + paramString) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, Error("Octopart API call error: " + resp.Status) + } + result := make(map[string]interface{}) + defer resp.Body.Close() + //body, err := ioutil.ReadAll(resp.Body) + //if err != nil { + // return nil, err + //} + //body = append(body, '\n') + //dec := json.NewDecoder(bytes.NewReader(body)) + dec := json.NewDecoder(resp.Body) + if err = dec.Decode(&result); err != nil { + return nil, err + } + return result, nil } // this method doesn't check internal cache, but it does append to it func (oc *OctopartClient) bomApiCall(manufacturers, mpns []string) ([]map[string]interface{}, error) { - // TODO: check len(mpns) == len(manufacturers) - queryList := make([]map[string]string, len(mpns)) - listItem := make(map[string]string) - for i, _ := range mpns { - listItem = make(map[string]string) - listItem["mpn_or_sku"] = mpns[i] - listItem["manufacturer"] = manufacturers[i] - listItem["limit"] = "1" - listItem["reference"] = manufacturers[i] + "|" + mpns[i] - queryList[i] = listItem - } + // TODO: check len(mpns) == len(manufacturers) + queryList := make([]map[string]string, len(mpns)) + listItem := make(map[string]string) + for i, _ := range mpns { + listItem = make(map[string]string) + listItem["mpn_or_sku"] = mpns[i] + listItem["manufacturer"] = manufacturers[i] + listItem["limit"] = "1" + listItem["reference"] = manufacturers[i] + "|" + mpns[i] + queryList[i] = listItem + } - linesBuffer := new(bytes.Buffer) - enc := json.NewEncoder(linesBuffer) - if err := enc.Encode(queryList); err != nil { - return nil, err - } + linesBuffer := new(bytes.Buffer) + enc := json.NewEncoder(linesBuffer) + if err := enc.Encode(queryList); err != nil { + return nil, err + } - response, err := oc.apiCall("bom/match", map[string]string{"lines": linesBuffer.String()}) - if err != nil { - return nil, err - } - // TODO: just grabbing first result for now; user can make better specification later - ret := make([]map[string]interface{}, len(mpns)) - for i, rawresult := range response["results"].([]interface{}) { - result := rawresult.(map[string]interface{}) - hits := int(result["hits"].(float64)) - reference := result["reference"].(string) - if hits == 0 { - ret[i] = nil - oc.infoCache[reference] = nil - } else { - ret[i] = result["items"].([]interface{})[0].(map[string]interface{}) - oc.infoCache[reference] = ret[i] - } - } - return ret, nil + response, err := oc.apiCall("bom/match", map[string]string{"lines": linesBuffer.String()}) + if err != nil { + return nil, err + } + // TODO: just grabbing first result for now; user can make better specification later + ret := make([]map[string]interface{}, len(mpns)) + for i, rawresult := range response["results"].([]interface{}) { + result := rawresult.(map[string]interface{}) + hits := int(result["hits"].(float64)) + reference := result["reference"].(string) + if hits == 0 { + ret[i] = nil + oc.infoCache[reference] = nil + } else { + ret[i] = result["items"].([]interface{})[0].(map[string]interface{}) + oc.infoCache[reference] = ret[i] + } + } + return ret, nil } -func (oc *OctopartClient) GetMarketInfo(manufacturers, mpns []string) ([]interface{}, error) { - if len(mpns) < 1 { - return nil, Error("no mpns strings passed in") - } - if len(mpns) != len(manufacturers) { - return nil, Error("number of mpns doesn't match number of manufacturers") - } - if len(mpns) > 100 { - return nil, Error("can't handle more than 100 queries at a time (yet)") - } - mpnToQuery := make([]string, 0) - manufacturersToQuery := make([]string, 0) - queryHash := "" - // check for queryHashes in internal cache - for i, _ := range mpns { - queryHash = manufacturers[i] + "|" + mpns[i] - if _, hasKey := oc.infoCache[queryHash]; hasKey != true { - manufacturersToQuery = append(manufacturersToQuery, manufacturers[i]) - mpnToQuery = append(mpnToQuery, mpns[i]) - } - } - // if necessary, fetch missing queryHashes to internal cache - if len(mpnToQuery) > 0 { - if _, err := oc.bomApiCall(manufacturersToQuery, mpnToQuery); err != nil { - return nil, err - } - } - // construct list of return info - result := make([]interface{}, len(mpns)) - for i, _ := range mpns { - queryHash = manufacturers[i] + "|" + mpns[i] - value, hasKey := oc.infoCache[queryHash] - if hasKey != true { - return nil, Error("key should be in cache, but isn't: " + queryHash) - } - result[i] = value - } - return result, nil +// this method checks the API query cache +func (oc *OctopartClient) GetMarketInfoList(manufacturers, mpns []string) ([]interface{}, error) { + if len(mpns) < 1 { + return nil, Error("no mpns strings passed in") + } + if len(mpns) != len(manufacturers) { + return nil, Error("number of mpns doesn't match number of manufacturers") + } + if len(mpns) > 100 { + return nil, Error("can't handle more than 100 queries at a time (yet)") + } + mpnToQuery := make([]string, 0) + manufacturersToQuery := make([]string, 0) + queryHash := "" + // check for queryHashes in internal cache + for i, _ := range mpns { + queryHash = manufacturers[i] + "|" + mpns[i] + if _, hasKey := oc.infoCache[queryHash]; hasKey != true { + manufacturersToQuery = append(manufacturersToQuery, manufacturers[i]) + mpnToQuery = append(mpnToQuery, mpns[i]) + } + } + // if necessary, fetch missing queryHashes remotely + if len(mpnToQuery) > 0 { + if _, err := oc.bomApiCall(manufacturersToQuery, mpnToQuery); err != nil { + return nil, err + } + } + // construct list of return info + result := make([]interface{}, len(mpns)) + for i, _ := range mpns { + queryHash = manufacturers[i] + "|" + mpns[i] + value, hasKey := oc.infoCache[queryHash] + if hasKey != true { + return nil, Error("key should be in cache, but isn't: " + queryHash) + } + result[i] = value + } + return result, nil } -func (oc *OctopartClient) GetReducedPricing(mpn, manufacturer string) (interface{}, error) { - marketInfo, err := oc.GetMarketInfo([]string{mpn}, []string{manufacturer}) +func (oc *OctopartClient) GetMarketInfo(manufacturer, mpn string) (map[string]interface{}, error) { + info, err := oc.GetMarketInfoList([]string{manufacturer}, []string{mpn}) if err != nil { return nil, err } - // reduce marketInfo to pricing - return marketInfo[0], nil + return info[0].(map[string]interface{}), nil +} + +func (oc *OctopartClient) GetExtraInfo(manufacturer, mpn string) (map[string]string, error) { + marketInfo, err := oc.GetMarketInfo(manufacturer, mpn) + if err != nil { + return nil, err + } + // extract market price, total avail, and "availability factor" from + // market info + log.Println(marketInfo) + ret := make(map[string]string) + ret["MarketPrice"] = marketInfo["avg_price"].(string) + ret["MarketFactor"] = marketInfo["market_availability"].(string) + ret["MarketTotalAvailable"] = marketInfo["total_avail"].(string) + return ret, nil } +func (oc *OctopartClient) AttachMarketInfo(li *LineItem) error { + if li.AggregateInfo == nil { + li.AggregateInfo = make(map[string]string) + } + extraInfo, err := oc.GetExtraInfo(li.Manufacturer, li.Mpn) + if err != nil { + log.Println(err.Error()) + return err + } + for key := range extraInfo { + li.AggregateInfo[key] = string(extraInfo[key]) + } + return nil +} + +func (oc *OctopartClient) AttachMarketInfoBom(b *Bom) error { + return nil +} -- cgit v1.2.3