// Query for a list of transactions. Admin-only for now. func handleQueryTrades(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) if !user.IsAdmin(c) { http.Error(w, "Action requires admin privileges", http.StatusForbidden) return } w.Header().Set("Access-Control-Allow-Origin", "*") limitStr := r.FormValue("limit") var limit int if limitStr == "" { limit = 1000 } else if n, err := strconv.ParseUint(limitStr, 10, 14); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else { limit = int(n) } articleStr := r.FormValue("article") var article bitwrk.ArticleId if articleStr == "" { http.Error(w, "article argument missing", http.StatusNotFound) return } else if err := checkArticle(c, articleStr); err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } else { article = bitwrk.ArticleId(articleStr) } periodStr := r.FormValue("period") if periodStr == "" { periodStr = "1d" } else if !resolutionExists(periodStr) { http.Error(w, "period unknown", http.StatusNotFound) return } period := resolutionByName(periodStr) // If begin is not given, calculate beginStr := r.FormValue("begin") var begin time.Time if beginStr == "" { begin = time.Now().Add(-period.interval) } else if t, err := time.Parse(time.RFC3339, beginStr); err != nil { http.Error(w, "Invalid begin time", http.StatusNotFound) return } else { begin = t } end := begin.Add(period.interval) unit := money.MustParseUnit("mBTC") buffer := new(bytes.Buffer) fmt.Fprintf(buffer, "{\"begin\": %#v, \"end\": %#v, \"unit\": \"%v\", \"data\": [\n", begin.Format(time.RFC3339), end.Format(time.RFC3339), unit) count := 0 priceSum := money.MustParse("BTC 0") feeSum := money.MustParse("BTC 0") firstLine := true handler := func(key string, tx bitwrk.Transaction) { var comma string if firstLine { firstLine = false comma = " " } else { comma = "," } fmt.Fprintf(buffer, "%v[% 5d, %#v, %v, %v, %v, \"%v\", \"%v\", %#v]\n", comma, count, tx.Matched.Format(time.RFC3339Nano), tx.Matched.UnixNano()/1000000, tx.Price.Format(unit, false), tx.Fee.Format(unit, false), tx.State, tx.Phase, key) priceSum = priceSum.Add(tx.Price) feeSum = feeSum.Add(tx.Fee) count++ } if err := db.QueryTransactions(c, limit, article, begin, end, handler); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) c.Errorf("Error querying transactions: %v", err) return } fmt.Fprintf(buffer, "], \"price_sum\": %v, \"fee_sum\": %v}\n", priceSum.Format(unit, false), feeSum.Format(unit, false)) // Write result back to requester w.Header().Set("Content-Type", "application/json") buffer.WriteTo(w) }
func handleQueryPrices(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") c := appengine.NewContext(r) needLogin := false articleStr := r.FormValue("article") var article bitwrk.ArticleId if articleStr == "" { http.Error(w, "article argument missing", http.StatusNotFound) return } else if err := checkArticle(c, articleStr); err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } else { article = bitwrk.ArticleId(articleStr) } periodStr := r.FormValue("period") if periodStr == "" { periodStr = "1d" } else if !resolutionExists(periodStr) { http.Error(w, "period unknown", http.StatusNotFound) return } period := resolutionByName(periodStr) resolutionStr := r.FormValue("resolution") if resolutionStr == "" { resolutionStr = "3m" } else if !resolutionExists(resolutionStr) { http.Error(w, "resolution unknown", http.StatusNotFound) return } resolution := resolutionByName(resolutionStr) if period.interval > 10000*resolution.interval { // User requested a lot of data... restrict for now needLogin = true } // If begin is not given, calculate from period beginStr := r.FormValue("begin") var begin time.Time if beginStr == "" { begin = time.Now().Add(-period.interval) } else if t, err := time.Parse(time.RFC3339, beginStr); err != nil { http.Error(w, "Invalid begin time", http.StatusNotFound) return } else { begin = t // Explicitly stating begin requires athentication for now needLogin = true } unitStr := r.FormValue("unit") var unit money.Unit if unitStr == "" { unit = money.MustParseUnit("mBTC") } else if u, err := money.ParseUnit(unitStr); err != nil { http.Error(w, "Invalid unit parameter", http.StatusNotFound) } else { unit = u } // Calculate end from begin and period end := begin.Add(period.interval) // Lookup coarsest tile resolution tile := resolution.coarsestTileResolution() if period.finerThan(tile) { tile = period } if tile.finerThan(resolution.finestTileResolution()) { tile = resolution.finestTileResolution() } // Truncate begin to a multiple of coarsest tile resolution. begin = begin.Truncate(tile.interval) // Enforce admin permissions if necessary if needLogin && !user.IsAdmin(c) { http.Error(w, "Action requires admin privileges", http.StatusForbidden) return } if prices, err := queryPrices(c, article, tile, resolution, begin, end); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else if r.FormValue("format") == "flot" { w.Header().Set("Content-Type", "application/json") renderPricesForFlot(w, prices, unit) } else if data, err := json.Marshal(prices); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else { // Write result back to requester w.Header().Set("Content-Type", "application/json") w.Write(data) } }