func main() { port := os.Getenv("PORT") if port == "" { log.Fatal("$PORT must be set") } db, err := sql.Open("postgres", os.Getenv("DATABASE_URL")) if err != nil { log.Fatalf("Error opening db: %q", err) } router := gin.Default() router.Use(gin.Logger()) router.LoadHTMLGlob("templates/*.tmpl.html") router.Static("/static", "static") router.GET("/", func(c *gin.Context) { decFactors := m.DefaultDecimationFactors() c.HTML(http.StatusOK, "index.tmpl.html", gin.H{ "title": "Spectrum Viewer", "dec_factors": decFactors, }) }) router.GET("/survey", func(c *gin.Context) { surveyHandler(c, db) }) router.GET("/sample", func(c *gin.Context) { sampleHandler(c, db) }) router.GET("/longsample", func(c *gin.Context) { longSampleHandler(c, db) }) authorized := router.Group("/priv") authorized.Use(TokenAuthMiddleware(db)) authorized.POST("/upload", func(c *gin.Context) { uploadHandler(c, db) }) router.Run(":" + port) }
func uploadHandler(c *gin.Context, db *sql.DB) { type IncomingUpload struct { Survey m.Survey `json:"survey"` Samples []m.SampleVector `json:"samples"` } var file multipart.File file, _, err := c.Request.FormFile("file") if err != nil { c.String(http.StatusBadRequest, "Bad request: %q", err) return } var incoming IncomingUpload buf := new(bytes.Buffer) buf.ReadFrom(file) if err := json.Unmarshal(buf.Bytes(), &incoming); err != nil { c.String(http.StatusBadRequest, "Invalid JSON: %q", err) return } incoming.Survey.RawData = string(buf.Bytes()) if uploaderId, err := strconv.Atoi(c.Request.Header["X-Uploader-Id"][0]); err != nil { c.String(http.StatusInternalServerError, "Missing header: X-Uploader-Id") return } else { incoming.Survey.UploaderId = uploaderId } insertDone := make(chan error) go func(db *sql.DB, inc *IncomingUpload, insertDone chan error) { var tx *sql.Tx tx, err := db.Begin() rollbackAndExit := func(err error) { if tx != nil { tx.Rollback() } log.Printf("DB Error: %q", err) insertDone <- err } if err != nil { rollbackAndExit(err) return } var surveyID int surveyID, err = inc.Survey.WriteToDB(tx) if err != nil { rollbackAndExit(err) return } var buffer bytes.Buffer preamble := `INSERT INTO sample (power, freq, bandwidth, decfactor, survey_id) VALUES ` batchSize := 500 // Decimate the samples with all default decimation factors for _, df := range m.DefaultDecimationFactors() { log.Printf("DF = %d", df) // Decimation var dSamples []m.Sample rawLen := len(inc.Samples) nBins := int(math.Ceil(float64(rawLen) / float64(df))) for i := 0; i < int(nBins); i++ { endIndex := int(df) * (i + 1) if endIndex > rawLen { endIndex = rawLen } binSamples := inc.Samples[int(df)*i : endIndex] sum := 0.0 for _, sample := range binSamples { sum += sample[0] // Power } dSamples = append(dSamples, m.Sample{ sum / float64(len(binSamples)), uint64((binSamples[0][1] + binSamples[len(binSamples)-1][1]) / 2), uint32(binSamples[0][2]) + uint32(binSamples[len(binSamples)-1][1]-binSamples[0][1]), }) } // Save to DB for i, sample := range dSamples { batchI := i % batchSize if batchI == 0 { // Beginning of batch buffer.Reset() buffer.WriteString(preamble) } buffer.WriteString( fmt.Sprintf("(%f, %d, %d, %d, %d)", sample.Power, sample.Freq, sample.Bandwidth, df, surveyID)) if batchI == batchSize-1 || i == len(dSamples)-1 { // End of batch if _, err := tx.Exec(buffer.String()); err != nil { rollbackAndExit(err) return } } else { buffer.WriteRune(',') } } } if err := tx.Commit(); err != nil { rollbackAndExit(err) return } insertDone <- nil }(db, &incoming, insertDone) if insertErr := <-insertDone; insertErr != nil { c.String(http.StatusInternalServerError, "DB Error: %q", insertErr) } else { c.String(http.StatusOK, "OK") } }