func getItemPrices( db evego.Database, mkt evego.Market, req *[]queryItem, station *evego.Station, loc string) (*map[string]responseItem, error) { respItems := make(map[string]responseItem) for _, i := range *req { dbItem, err := db.ItemForName(i.ItemName) if err != nil { continue } var ( item responseItem orders *[]evego.Order ) if station != nil { orders, err = mkt.OrdersInStation(dbItem, station) } else { orders, err = mkt.OrdersForItem(dbItem, loc, evego.AllOrders) } if err != nil { return nil, fmt.Errorf("Unable to retrieve order information for %v: %v", dbItem.Name, err) } item = summarizeOrders(db, *orders, dbItem) respItems[item.ItemName] = item } return &respItems, nil }
// UnusedSalvage returns a web handler function that identifies a user's salvage // drops that are unused by any blueprint they own. func UnusedSalvage(localdb db.LocalDB, sde evego.Database, sess server.Sessionizer) web.HandlerFunc { return func(c web.C, w http.ResponseWriter, r *http.Request) { s := sess.GetSession(&c, w, r) myUserID := s.User charID, err := strconv.Atoi(c.URLParams["charID"]) if err != nil { http.Error(w, `{"status": "Error", "error": "Invalid character ID supplied."}`, http.StatusBadRequest) return } salvage, err := localdb.UnusedSalvage(myUserID, charID) if err != nil { http.Error(w, `{"status": "Error", "error": "Unable to access database."}`, http.StatusInternalServerError) log.Printf("Error accessing database with user %v, character %v: %v", myUserID, charID, err) return } stations := make(map[string]*evego.Station) itemInfo := make(map[string]*evego.Item) for i := range salvage { item := &salvage[i] if _, found := stations[strconv.Itoa(item.StationID)]; !found { stn, err := localdb.StationForID(item.StationID) if err == nil { stations[strconv.Itoa(item.StationID)] = stn } else { // This should really not friggin' happen. http.Error(w, `{"status": "Error", "error": "Unable to look up station/outpost."}`, http.StatusInternalServerError) log.Printf("Unable to look up station/outpost ID %v: %v", item.StationID, err) return } } typeIDStr := strconv.Itoa(item.TypeID) if _, found := itemInfo[typeIDStr]; !found { thisItem, err := sde.ItemForID(item.TypeID) if err == nil { itemInfo[typeIDStr] = thisItem } } } response := struct { Items []evego.InventoryItem `json:"items"` Stations map[string]*evego.Station `json:"stations"` ItemInfo map[string]*evego.Item `json:"itemInfo"` }{salvage, stations, itemInfo} salvageJSON, err := json.Marshal(&response) if err != nil { http.Error(w, `{"status": "Error", "error": "Unable to marshal JSON."}`, http.StatusInternalServerError) log.Printf("Error marshalling JSON unused salvage with user %v, character %v: %v", myUserID, charID, err) return } w.Write(salvageJSON) return } }
// ItemsMarketValue returns a handler that takes as input a JSON // array of items and their quantities, plus a specified station // or region, and computes the items' value. // // FIXME: Should return all buy orders within range for the queried system. func ItemsMarketValue(db evego.Database, mkt evego.Market, xmlAPI evego.XMLAPI) web.HandlerFunc { return func(c web.C, w http.ResponseWriter, r *http.Request) { contentType := r.Header.Get("Content-Type") contentType, _, err := mime.ParseMediaType(contentType) if err != nil { http.Error(w, `{"status": "Error", "error": "Bad request content type"}`, http.StatusBadRequest) return } if contentType != "application/json" { http.Error(w, `{"status": "Error", "error": "Request must be of type application/json"}`, http.StatusUnsupportedMediaType) return } reqBody, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, `{"status": "Error", "error": "Unable to process request body"}`, http.StatusBadRequest) return } var req []queryItem err = json.Unmarshal(reqBody, &req) if err != nil { http.Error(w, fmt.Sprintf("Unable to process request JSON: %v", err), http.StatusBadRequest) w.Write([]byte(`{"status": "Error"}`)) return } loc := c.URLParams["location"] stationIDStr, isStation := c.URLParams["id"] var station *evego.Station if isStation { // Get station / outpost object. stationID, _ := strconv.Atoi(stationIDStr) station, err = db.StationForID(stationID) if err != nil { // Not a station; should be an outpost. station, err = xmlAPI.OutpostForID(stationID) if err != nil { http.Error(w, `{"status": "Error", "error": "Unable to identify location"}`, http.StatusBadRequest) return } } } respItems, err := getItemPrices(db, mkt, &req, station, loc) if err != nil { http.Error(w, `{"status": "Error", "error": "Unable to retrieve order information"}`, http.StatusBadRequest) return } respJSON, _ := json.Marshal(respItems) w.Write(respJSON) } }
// Convert the station/outpost object provided by evego's API into a more // useful JSON object to be sent to the client. func stationFromAPI(db evego.Database, s *evego.Station, isOutpost bool) station { system, _ := db.SolarSystemForID(s.SystemID) stn := station{ Name: s.Name, ID: s.ID, SystemName: system.Name, Constellation: system.Constellation, Region: system.Region, Owner: s.Corporation, OwnerID: s.CorporationID, ReprocessingEfficiency: s.ReprocessingEfficiency, } // Calculate rounded security as displayed in client—see // http://wiki.eveuniversity.org/System_Security for rules. if system.Security >= 0.05 || system.Security < 0.00 { // high or low based on rounding. stn.Security = roundSecurity(system.Security) } else { // lowsec, not nullsec—rounds up. stn.Security = 0.1 } if isOutpost { stn.Outpost = true // Reprocessing efficiency for outposts isn't provided in the SDE, // so we default to a basic station. stn.ReprocessingEfficiency = 0.50 } return stn }
// ReprocessOutputValues returns a web handler function that generates a list of // possible output from reprocessing, along with the Jita sell and buy price of each. func ReprocessOutputValues(db evego.Database, mkt evego.Market, xmlAPI evego.XMLAPI, cache evego.Cache) web.HandlerFunc { tickerboard := []queryItem{ {Quantity: 1, ItemName: "Tritanium"}, {Quantity: 1, ItemName: "Pyerite"}, {Quantity: 1, ItemName: "Mexallon"}, {Quantity: 1, ItemName: "Isogen"}, {Quantity: 1, ItemName: "Nocxium"}, {Quantity: 1, ItemName: "Megacyte"}, {Quantity: 1, ItemName: "Zydrine"}, {Quantity: 1, ItemName: "Megacyte"}, } return func(c web.C, w http.ResponseWriter, r *http.Request) { // Check cached and use that instead if it's available. cached, found := cache.Get(reprocessCacheKey) if found { w.Write(cached) return } jita, err := db.StationForID(60003760) // Jita IV - Moon 4 - Caldari Navy Assembly Plant if err != nil { http.Error(w, `{"status": "Error", "error": "Unable to retrieve ticker prices"}`, http.StatusInternalServerError) log.Printf("Error looking up Jita station info (???): %v", err) return } results, err := getItemPrices(db, mkt, &tickerboard, jita, "") if err != nil { http.Error(w, `{"status": "Error", "error": "Unable to retrieve ticker prices"}`, http.StatusInternalServerError) log.Printf("Error getting Jita item prices: %v", err) return } resultsJSON, err := json.Marshal(results) // Write output to cache as well. cache.Put(reprocessCacheKey, resultsJSON, time.Now().Add(reprocessCacheTTL)) w.Write(resultsJSON) } }
func autocompleteSystems(db evego.Database, search string) *[]evego.SolarSystem { // Use make to ensure that we actually have a slice rather than just a nil // pointer. results := make([]evego.SolarSystem, 0, 5) log.Printf("searching %v\n", search) systems, _ := db.SolarSystemsForPattern(search + "%") for i, s := range systems { if i >= 10 { break } results = append(results, s) } return &results }
// ReprocessItems returns a handler function that takes as input an item list // and returns the reprocessing output of each inventory line. func ReprocessItems(db evego.Database, mkt evego.Market) web.HandlerFunc { jita, err := db.StationForID(60003760) // Jita IV - Moon 4 - Caldari Navy Assembly Plant if err != nil { log.Fatalf("Seriously, guys, something's gone wrong with the database!") } return func(c web.C, w http.ResponseWriter, r *http.Request) { contentType := r.Header.Get("Content-Type") contentType, _, err := mime.ParseMediaType(contentType) if err != nil { http.Error(w, "Bad request content type", http.StatusBadRequest) w.Write([]byte(`{"status": "Error"}`)) return } if contentType != "application/json" { http.Error(w, "Request must be of type application/json", http.StatusUnsupportedMediaType) w.Write([]byte(`{"status": "Error"}`)) return } reqBody, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, "Unable to process request body", http.StatusBadRequest) w.Write([]byte(`{"status": "Error"}`)) return } var req reproQuery err = json.Unmarshal(reqBody, &req) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) w.Write([]byte(`{"status": "Error"}`)) return } // Convert 0..100 scale to 0..1. stationYield := math.Max(math.Min(1, req.StationYield*0.01), 0) taxRate := math.Max(math.Min(1, req.TaxRate*0.01), 0) reproSkills := industry.ReproSkills{ ScrapmetalProcessing: req.ScrapmetalSkill, } results := make(map[string][]evego.InventoryLine) for _, i := range req.Items { item, err := db.ItemForName(i.ItemName) if err != nil { continue } itemResults, err := industry.ReprocessItem(db, item, i.Quantity, stationYield, taxRate, reproSkills) if err != nil { http.Error(w, "Unable to compute reprocessing output", http.StatusInternalServerError) w.Write([]byte(`{"status": "Error"}`)) return } results[item.Name] = itemResults } prices := make(map[string]responseItem) // Loop over each item that was reprocessed. for _, itemOut := range results { // For each item in its component materials, for _, item := range itemOut { // Check if we already know its price in Jita itemName := item.Item.Name _, found := prices[itemName] if !found { // If not there, get its price. myPrices, err := getItemPrices(db, mkt, &[]queryItem{{Quantity: 1, ItemName: itemName}}, jita, "") if err != nil { http.Error(w, `{"status": "Error", "error": "Unable to look up prices (reprocessing)"}`, http.StatusInternalServerError) return } prices[itemName] = (*myPrices)[itemName] } } } response := reproResults{ Items: results, Prices: prices, } resultsJSON, _ := json.Marshal(response) w.Write(resultsJSON) } }