func UploadHandler(w http.ResponseWriter, r *http.Request) { switch r.Method { //GET displays the upload form. case "GET": display(w, "uploader", nil) //POST takes the uploaded file(s) and saves it to disk. case "POST": //parse the multipart form in the request err := r.ParseMultipartForm(1000000) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } //get a ref to the parsed multipart form m := r.MultipartForm //get the *fileheaders files := m.File["myfiles"] for i, _ := range files { //for each fileheader, get a handle to the actual file file, err := files[i].Open() defer file.Close() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } //create destination file making sure the path is writeable. dst, err := os.Create("C:/Users/Administrator/git-projects/jps-frontend/uploads/" + files[i].Filename) defer dst.Close() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } //copy the uploaded file to the destination file if _, err := io.Copy(dst, file); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } fmt.Printf("%v\n", files[i].Header) fit.Parse("C:/Users/Administrator/git-projects/jps-frontend/uploads/"+files[i].Filename, true) } //display success message. display(w, "uploader", "Upload successful.") default: w.WriteHeader(http.StatusMethodNotAllowed) } }
// Parse the uploaded file, parse it and return run information suitable // to construct the user interface. func plotHandler(w http.ResponseWriter, r *http.Request) { type Plotvals struct { Titletext string XName string Y0Name string Y1Name string Y2Name string DispDistance []float64 DispPace []float64 DispAltitude []float64 DispCadence []float64 TimeStamps []int64 Latlongs []map[string]float64 LapDist []float64 LapTime []string LapCal []float64 LapPace []string C0Str string C1Str string C2Str string C3Str string C4Str string TotalDistance string TotalPace string ElapsedTime string TotalCal string AvgPower string StartDateStamp string EndDateStamp string Device string PredictedTimes map[string]string TrainingPaces map[string]string VDOT float64 DeviceName string DeviceUnitID string DeviceProdID string RunScore float64 VO2max float64 Buildstamp string Githash string } var xStr string = "Distance " var y0Str string = "Pace " var y1Str string = "Elevation " var y2Str string = "Cadence " var runRecs []fit.Record var runLaps []fit.Lap var c0Str, c1Str, c2Str, c3Str, c4Str string var fitStruct fit.FitFile var db *tcx.TCXDB = nil // What has the user selected for unit system? toEnglish = true param1s := r.URL.Query()["toEnglish"] if param1s != nil { if param1s[0] == "true" { toEnglish = true } if param1s[0] == "false" { toEnglish = false } } // What race time/distance has the user entered? var racedist float64 var racehours, racemins, racesecs int64 param2s := r.URL.Query()["racedist"] if param2s != nil { racedist, _ = strconv.ParseFloat(param2s[0], 64) } else { racedist = 5000.0 } param3s := r.URL.Query()["racehours"] if param3s != nil { racehours, _ = strconv.ParseInt(param3s[0], 10, 64) } else { racehours = 0 } param4s := r.URL.Query()["racemins"] if param4s != nil { racemins, _ = strconv.ParseInt(param4s[0], 10, 64) } else { racemins = 25 } param5s := r.URL.Query()["racesecs"] if param5s != nil { racesecs, _ = strconv.ParseInt(param5s[0], 10, 64) } else { racesecs = 0 } // Calculate analysis results based on segment or whole run? useSegment := false param6s := r.URL.Query()["useSegment"] if param6s != nil { if param6s[0] == "true" { useSegment = true } if param6s[0] == "false" { useSegment = false } } // What split time/distance has the user entered? var splitdist float64 var splithours, splitmins, splitsecs int64 param7s := r.URL.Query()["splitdist"] if param7s != nil { splitdist, _ = strconv.ParseFloat(param7s[0], 64) } else { splitdist = 5000.0 } param8s := r.URL.Query()["splithours"] if param8s != nil { splithours, _ = strconv.ParseInt(param8s[0], 10, 64) } else { splithours = 0 } param9s := r.URL.Query()["splitmins"] if param9s != nil { splitmins, _ = strconv.ParseInt(param9s[0], 10, 64) } else { splitmins = 25 } param10s := r.URL.Query()["splitsecs"] if param10s != nil { splitsecs, _ = strconv.ParseInt(param10s[0], 10, 64) } else { splitsecs = 0 } /* dump, err := httputil.DumpRequest(r, true) if err != nil { http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) return } fmt.Printf("%s\n\n", dump) */ // Read file. uploadFname gets set in uploadHandler. b, _ := ioutil.ReadFile(uploadFname) rslt := http.DetectContentType(b) switch { case rslt == "application/octet-stream": // Filetype is FIT, or at least it could be? fitStruct = fit.Parse(uploadFname, false) runRecs = fitStruct.Records runLaps = fitStruct.Laps case rslt == "text/xml; charset=utf-8": // Filetype is TCX or at least it could be? db, _ = tcx.ReadTCXFile(uploadFname) // if err != nil { // fmt.Printf("Error parsing file", err) // } // We cleverly convert the values of interest into a structures we already // can handle. runRecs = tcx.CvtToFitRecs(db) runLaps = tcx.CvtToFitLaps(db) } // Build the variable strings based on unit system. if toEnglish { xStr = xStr + "(mi)" y0Str = y0Str + "(min/mi)" y1Str = y1Str + "(ft)" y2Str = y2Str + "(steps/min)" c0Str = "Lap" c1Str = "Distance" + "(mi)" c2Str = "Pace" + "(min/mi)" c3Str = "Time" + "(min)" c4Str = "Calories" + "(kcal)" } else { xStr = xStr + "(km)" y0Str = y0Str + "(min/km)" y1Str = y1Str + "(m)" y2Str = y2Str + "(steps/min)" c0Str = "Lap" c1Str = "Distance" + "(km)" c2Str = "Pace" + "(min/km)" c3Str = "Time" + "(min)" c4Str = "Calories" + "(kcal)" } // Create an object to contain various plot values. p := Plotvals{Titletext: "", XName: xStr, Y0Name: y0Str, Y1Name: y1Str, Y2Name: y2Str, DispDistance: nil, DispPace: nil, DispAltitude: nil, DispCadence: nil, TimeStamps: nil, Latlongs: nil, LapDist: nil, LapTime: nil, LapCal: nil, LapPace: nil, C0Str: c0Str, C1Str: c1Str, C2Str: c2Str, C3Str: c3Str, C4Str: c4Str, TotalDistance: "", TotalPace: "", ElapsedTime: "", TotalCal: "", AvgPower: "", StartDateStamp: "", EndDateStamp: "", Device: "", PredictedTimes: nil, TrainingPaces: nil, VDOT: 0.0, DeviceName: "", DeviceUnitID: "", DeviceProdID: "", RunScore: 0.0, VO2max: 0.0, } if rslt == "application/octet-stream" { p.DeviceName = fitStruct.DeviceInfo[0].Manufacturer p.DeviceProdID = fitStruct.DeviceInfo[0].Product p.DeviceUnitID = fmt.Sprint(fitStruct.DeviceInfo[0].Serial_number) } if rslt == "text/xml; charset=utf-8" { p.DeviceName, p.DeviceUnitID, p.DeviceProdID = tcx.DeviceInfo(db) } // Here's where the heavy lifting of pulling tracks and performance information // from (portions of) the fit file into something we can view is done. p.Latlongs, p.TimeStamps, p.DispDistance, p.DispPace, p.DispAltitude, p.DispCadence = processFitRecord(runRecs, toEnglish) p.LapDist, p.LapTime, p.LapCal, p.LapPace = processFitLap(runLaps, toEnglish) // Get the start time. p.Titletext += time.Unix(p.TimeStamps[0], 0).Format(time.UnixDate) // Calculate the summary string information. p.TotalDistance, p.TotalPace, p.ElapsedTime, p.TotalCal, p.AvgPower, p.StartDateStamp, p.EndDateStamp = createStats(toEnglish, p.DispDistance, p.TimeStamps, p.LapCal) // Calculate the analysis page. p.PredictedTimes, p.VDOT, p.VO2max, p.RunScore, p.TrainingPaces = createAnalysis(toEnglish, useSegment, p.DispDistance, p.TimeStamps, splitdist, splithours, splitmins, splitsecs, racedist, racehours, racemins, racesecs) //Convert to json. js, err := json.Marshal(p) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } //fmt.Println("plotHandler Received Request") w.Header().Set("Content-Type", "text/json") w.Header().Set("Access-Control-Allow-Origin", "*") //Send w.Write(js) }