func view(user types.UserSettings, filter Filter) (p Page) { //get ff data - would be good not to get this on every activity call ******************** var ffData []types.Ff_data_point if filter.ShowTss { ffData = ff(user, filter) } //power curve var cpData []Cp3 var legends Cp3Legend if filter.ShowMmp { cpData, legends = powercurve(user, filter) } //Heart vs Power var hvpData []Hvp if filter.ShowHvp { hvpData = hvp(user, filter) } var hvp_label string if filter.HvpFrom == 0 && filter.HvpTo == 0 { hvp_label = "All ride durations" } else { hvp_label = "Rides with durations between <b>" + strconv.Itoa(filter.HvpFrom) + "</b> and <b>" + strconv.Itoa(filter.HvpTo) + "</b> minutes" } //Tss vs Duration var tvdData []Tvd var tvdLegend string if filter.ShowDur { tvdData, tvdLegend = tvd(user, filter) } //these two are well merged as a function so only don't process if both not required. else just hide the appropriate graphs var hbzData []Hbz var pbzData []Pbz if !filter.ShowHbz && !filter.ShowPbz { //these two are well merged as a function so only don't process if both not required. else just hide the appropriate graphs } else { hbzData, pbzData = hpbz(user, filter) } var zoneLabels types.ZoneLabels zoneLabels.PowerZ1 = int(0.55 * float64(user.Ftp)) zoneLabels.PowerZ2 = int(0.74 * float64(user.Ftp)) zoneLabels.PowerZ3 = int(0.89 * float64(user.Ftp)) zoneLabels.PowerZ4 = int(1.04 * float64(user.Ftp)) zoneLabels.PowerZ5 = int(1.2 * float64(user.Ftp)) zoneLabels.HeartZ1 = int(0.81 * float64(user.Thr)) zoneLabels.HeartZ2 = int(0.89 * float64(user.Thr)) zoneLabels.HeartZ3 = int(0.93 * float64(user.Thr)) zoneLabels.HeartZ4 = int(0.99 * float64(user.Thr)) zoneLabels.HeartZ5a = int(1.02 * float64(user.Thr)) zoneLabels.HeartZ5b = int(1.06 * float64(user.Thr)) //cp data - doesn't actually need to be reversed (corrected), but just wanted to for future flexibility cpDataRev := make([]Cp3, 0) for i := len(cpData) - 1; i >= 0; i-- { cpDataRev = append(cpDataRev, cpData[i]) } selectHTML := template.HTML("") for _, userval := range user.StandardRides { //for each of the user's standard rides selected := "" for _, filterval := range filter.StandardRides { //test for a filter match if userval.Id == filterval { selected = "selected" } } selectHTML += template.HTML("<option " + selected + " value=" + strconv.Itoa(userval.Id) + ">" + userval.Label + "</option>") } p = Page{ FfData: ffData, CpData: cpDataRev, HvpData: hvpData, TvdData: tvdData, TvdLegend: tvdLegend, CpLegend1: legends.Series1, CpLegend2: legends.Series2, CpLegend3: legends.Series3, HvpLabel: hvp_label, HbzData: hbzData, PbzData: pbzData, Settings: user, Filter: filter, ZoneLabels: zoneLabels, StandardRidesHTML: selectHTML, } return }
//get the week's activities func dashboard(user types.UserSettings) (types.Tvd, types.Zones, types.ZoneLabels) { user_id := user.Id tvd_data_points := make([]types.Tvd_data_point, 0) tvd_data := make([]types.Tvd, 0) var user_data types.Metrics var activity_id string var end_summary_json []byte var activity_start time.Time var heart_json []byte var power_json []byte var cur_ftp int var cur_thr int var power_series []int var heart_series []int var has_power, has_heart bool var zoneData types.Zones cluster := gocql.NewCluster(config.DbHost) cluster.Keyspace = "joulepersecond" cluster.Consistency = gocql.Quorum session, _ := cluster.CreateSession() defer session.Close() //get all of the user's data (at least all for now) TODO limit these queries by date if poss. Done! timeNow := time.Now() //we can use TimeOffset to test from other dates timeNow = timeNow.AddDate(0, 0, user.TimeOffset) //timeTruncated is a time at the beginning of the day timeTruncated := timeNow.Truncate(time.Hour * 24) //we will use timeThen to refer to the beginning of the current week var timeThen time.Time dayOfWeek := int(timeTruncated.Weekday()) if int(timeTruncated.Weekday()) != 0 { //if not equal to Sunday... timeThen = timeTruncated.AddDate(0, 0, -(dayOfWeek - 1)) //fetch records for the week so far (second -1 to start from Monday) } else { timeThen = timeTruncated.AddDate(0, 0, -6) //if today is Sunday, query back to Monday } iter := session.Query(`SELECT activity_id, activity_start, end_summary_json FROM joulepersecond.user_activity WHERE user_id = ? AND activity_start <=? AND activity_start >= ? `, user_id, timeNow, timeThen).Iter() for iter.Scan(&activity_id, &activity_start, &end_summary_json) { var tvd_data_point types.Tvd_data_point json.Unmarshal(end_summary_json, &user_data) tvd_data_point.Date = user_data.StartTime tvd_data_point.Dur = user_data.Dur if user_data.Utss > 0 { tvd_data_point.Tss = user_data.Utss } else if user_data.Tss > 0 { tvd_data_point.Tss = user_data.Tss } else if user_data.Etss > 0 { tvd_data_point.Tss = user_data.Etss } else { tvd_data_point.Tss = 0 } tvd_data_points = append(tvd_data_points, tvd_data_point) //for each activity, get the exended data iter := session.Query(`SELECT power_json, heart_json, end_summary_json, has_power, has_heart, cur_ftp, cur_thr FROM joulepersecond.proc_activity WHERE activity_id = ? `, activity_id).Iter() for iter.Scan(&power_json, &heart_json, &end_summary_json, &has_power, &has_heart, &cur_ftp, &cur_thr) { json.Unmarshal(end_summary_json, &user_data) json.Unmarshal(power_json, &power_series) json.Unmarshal(heart_json, &heart_series) var samples int if has_power { samples = len(power_series) has_power = true } if has_heart { samples = len(heart_series) has_heart = true } if !has_heart && !has_power { break } if has_power { zoneData.HasPower = true var sum int var average float64 for i := user.SampleSize; i < samples; i++ { //reset total sum = 0 //get thirty second rolling slice rollingPowerSlice := power_series[i-user.SampleSize : i] for _, val := range rollingPowerSlice { //sum the sliding slice values sum += val } average = float64(sum / user.SampleSize) if average < 0.55*float64(cur_ftp) { zoneData.Z1++ } else if average > 0.55*float64(cur_ftp) && average <= 0.74*float64(cur_ftp) { zoneData.Z2++ } else if average > 0.74*float64(cur_ftp) && average <= 0.89*float64(cur_ftp) { zoneData.Z3++ } else if average > 0.89*float64(cur_ftp) && average <= 1.04*float64(cur_ftp) { zoneData.Z4++ } else if average > 1.04*float64(cur_ftp) && average <= 1.2*float64(cur_ftp) { zoneData.Z5++ } else if average > 1.2*float64(cur_ftp) { zoneData.Z6++ } } } //loop through each sample and post the value into the correct pidgeon hole if has_heart { zoneData.HasHeart = true for i := 0; i < samples; i++ { if float64(heart_series[i]) < 0.81*float64(cur_thr) { zoneData.HR1++ } else if float64(heart_series[i]) > 0.81*float64(cur_thr) && float64(heart_series[i]) <= 0.89*float64(cur_thr) { zoneData.HR2++ } else if float64(heart_series[i]) > 0.89*float64(cur_thr) && float64(heart_series[i]) <= 0.93*float64(cur_thr) { zoneData.HR3++ } else if float64(heart_series[i]) > 0.93*float64(cur_thr) && float64(heart_series[i]) <= 0.99*float64(cur_thr) { zoneData.HR4++ } else if float64(heart_series[i]) > 0.99*float64(cur_thr) && float64(heart_series[i]) <= 1.02*float64(cur_thr) { zoneData.HR5a++ } else if float64(heart_series[i]) > 1.02*float64(cur_thr) && float64(heart_series[i]) <= 1.06*float64(cur_thr) { zoneData.HR5b++ } else if float64(heart_series[i]) > 1.06*float64(cur_thr) { zoneData.HR5c++ } } } } } //we now have all the data... Now sort it sumTss := 0 var sumDur time.Duration var summedWeeklyTvd types.Tvd //loop through each retrieved activity for i := 0; i < len(tvd_data_points); i++ { tvd_data = append(tvd_data, summedWeeklyTvd) //sum the values sumTss += tvd_data_points[i].Tss sumDur += tvd_data_points[i].Dur } summedWeeklyTvd.TotalTss = sumTss summedWeeklyTvd.TotalDur = utility.Round(sumDur.Hours(), .5, 2) var zoneLabels types.ZoneLabels zoneLabels.PowerZ1 = int(0.55 * float64(user.Ftp)) zoneLabels.PowerZ2 = int(0.74 * float64(user.Ftp)) zoneLabels.PowerZ3 = int(0.89 * float64(user.Ftp)) zoneLabels.PowerZ4 = int(1.04 * float64(user.Ftp)) zoneLabels.PowerZ5 = int(1.2 * float64(user.Ftp)) zoneLabels.HeartZ1 = int(0.81 * float64(user.Thr)) zoneLabels.HeartZ2 = int(0.89 * float64(user.Thr)) zoneLabels.HeartZ3 = int(0.93 * float64(user.Thr)) zoneLabels.HeartZ4 = int(0.99 * float64(user.Thr)) zoneLabels.HeartZ5a = int(1.02 * float64(user.Thr)) zoneLabels.HeartZ5b = int(1.06 * float64(user.Thr)) //get the power and heartrate zone data return summedWeeklyTvd, zoneData, zoneLabels }