public override string GetText(object element, TreeList.Column column) { Data.TrailResult row = (element as TrailResultWrapper).Result; //Some special for Summary if (row is SummaryTrailResult) { switch (column.Id) { case TrailResultColumnIds.AveragePowerBalance: case TrailResultColumnIds.AverageTemperature: case TrailResultColumnIds.AverageGroundContactTime: case TrailResultColumnIds.AverageVerticalOscillation: case TrailResultColumnIds.AverageSaturatedHemoglobin: case TrailResultColumnIds.AverageTotalHemoglobinConcentration: //No implementation, ignore return(null); default: if (!Controller.TrailController.Instance.ExplicitSelection && TrailResultColumnIds.ClimbFields.Contains(column.Id) && !row.ClimbCalculated) { //Potentially many selections (of Ascent etc), no value set return(null); } break; } if (TrailsPlugin.Data.Settings.ResultSummaryStdDev) { SummaryTrailResult row2 = row as SummaryTrailResult; if (!row2.IsTotal && row2.Results.Count > 1) { switch (column.Id) { case TrailResultColumnIds.StartTime: //Not interesting to average time when only one activity. Other multi may be interesting. if (row2.Activities.Count <= 1) { return(null); } //Only time of day, averaged return(row.StartTime.ToLocalTime().ToString("T")); case TrailResultColumnIds.Duration: { SummaryValue <TimeSpan> a = row2.DurationStdDev(); return(UnitUtil.Time.ToString(a.Value, "") + " σ" + UnitUtil.Time.ToString(a.StdDev, "")); } case TrailResultColumnIds.Distance: { SummaryValue <double> a = row2.DistanceStdDev(); string d; if (row.PoolLengthInfo != null) { d = UnitUtil.Distance.ToString(a.Value, row.PoolLengthInfo.DistanceUnits, "F0"); } else { d = UnitUtil.Distance.ToString(a.Value, Controller.TrailController.Instance.ReferenceActivity, ""); } return(d + " σ" + UnitUtil.Elevation.ToString(a.StdDev, Controller.TrailController.Instance.ReferenceActivity, "")); } case TrailResultColumnIds.AvgPace: { SummaryValue <double> a = row2.AvgPaceStdDev(); return(UnitUtil.Pace.ToString(a.Value, Controller.TrailController.Instance.ReferenceActivity, "") + " σ" + UnitUtil.Pace.ToString(a.StdDev, Controller.TrailController.Instance.ReferenceActivity, "")); } case TrailResultColumnIds.AvgSpeed: { SummaryValue <double> a = row2.AvgSpeedStdDev(); return(UnitUtil.Speed.ToString(a.Value, Controller.TrailController.Instance.ReferenceActivity, "") + " σ" + UnitUtil.Speed.ToString(a.StdDev, Controller.TrailController.Instance.ReferenceActivity, "")); } case TrailResultColumnIds.AvgSpeedPace: { SummaryValue <double> a; if (UnitUtil.PaceOrSpeed.IsPace(Controller.TrailController.Instance.ReferenceActivity)) { a = row2.AvgPaceStdDev(); return(UnitUtil.Pace.ToString(a.Value, Controller.TrailController.Instance.ReferenceActivity, "") + " σ" + UnitUtil.Pace.ToString(a.StdDev, Controller.TrailController.Instance.ReferenceActivity, "")); } else { a = row2.AvgSpeedStdDev(); return(UnitUtil.Speed.ToString(a.Value, Controller.TrailController.Instance.ReferenceActivity, "") + " σ" + UnitUtil.Speed.ToString(a.StdDev, Controller.TrailController.Instance.ReferenceActivity, "")); } } //case TrailResultColumnIds.GradeRunAdjustedTime: // { // SummaryValue<double> a = row2.GradeRunAdjustedTimeStdDev(); // return UnitUtil.Time.ToString(a.Value, "") + " σ" + UnitUtil.Time.ToString(a.StdDev, ""); // } //case TrailResultColumnIds.GradeRunAdjustedPace: // { // SummaryValue<TimeSpan> a = row2.GradeRunAdjustedPaceStdDev(); // return UnitUtil.Pace.ToString(a.Value, Controller.TrailController.Instance.ReferenceActivity, "") + " σ" + UnitUtil.Pace.ToString(a.StdDev, Controller.TrailController.Instance.ReferenceActivity, ""); // } //case TrailResultColumnIds.Diff: // { // SummaryValue<double> a = row2.DiffStdDev(); // return UnitUtil.Elevation.ToString(a.Value, Controller.TrailController.Instance.ReferenceActivity, "") + " σ" + UnitUtil.Elevation.ToString(a.StdDev, Controller.TrailController.Instance.ReferenceActivity, ""); // } default: break; } } } } if (row is PausedChildTrailResult) { switch (column.Id) { //Some paused fields should not be visible case TrailResultColumnIds.Order: return(null); default: break; } } if (row is SubChildTrailResult) { switch (column.Id) { //Ignore wildly inaccurate data, few points for Pool swimming, can be lower than Avg //(not always good on lap level too) case TrailResultColumnIds.FastestSpeed: case TrailResultColumnIds.FastestPace: return(null); default: break; } } switch (column.Id) { case TrailResultColumnIds.ResultColor: return(null); //String output without formatting case TrailResultColumnIds.Order: case TrailResultColumnIds.Name: case TrailResultColumnIds.TrailName: return(base.GetText(row, column)); case TrailResultColumnIds.StartTime: if (row.Activity == null) { return(null); } string date = ""; if (m_multiple) { date = row.StartTime.ToLocalTime().ToShortDateString() + " "; } return(date + row.StartTime.ToLocalTime().ToString("T")); case TrailResultColumnIds.StartDistance: if (row.PoolLengthInfo != null) { return(UnitUtil.Distance.ToString(row.StartDistance, row.PoolLengthInfo.DistanceUnits, "F0u")); } else { return(UnitUtil.Distance.ToString(row.StartDistance, Controller.TrailController.Instance.ReferenceActivity, "")); } case TrailResultColumnIds.EndTime: if (row.Activity == null) { return(null); } return(row.EndTime.ToLocalTime().ToString("T")); case TrailResultColumnIds.Duration: return(UnitUtil.Time.ToString(row.Duration, "")); case TrailResultColumnIds.Distance: if (row.PoolLengthInfo != null) { return(UnitUtil.Distance.ToString(row.Distance, row.PoolLengthInfo.DistanceUnits, "F0u")); } else { return(UnitUtil.Distance.ToString(row.Distance, Controller.TrailController.Instance.ReferenceActivity, "")); } case TrailResultColumnIds.AvgCadence: return(UnitUtil.Cadence.ToString(row.AvgCadence)); case TrailResultColumnIds.AvgHR: return(UnitUtil.HeartRate.ToString(row.AvgHR)); case TrailResultColumnIds.MaxHR: return(UnitUtil.HeartRate.ToString(row.MaxHR)); case TrailResultColumnIds.Ascent: return(UnitUtil.Elevation.ToString(row.Ascent)); case TrailResultColumnIds.Descent: return(UnitUtil.Elevation.ToString(row.Descent)); case TrailResultColumnIds.ElevChg: return((row.ElevChg > 0 ? "+" : "") + UnitUtil.Elevation.ToString(row.ElevChg, "")); case TrailResultColumnIds.AvgPower: return(UnitUtil.Power.ToString(row.AvgPower)); case TrailResultColumnIds.AscAvgGrade: return((row.AscAvgGrade).ToString("0.0%")); case TrailResultColumnIds.AscMaxAvgGrade: return((row.AscMaxAvgGrade).ToString("0.0%")); case TrailResultColumnIds.DescAvgGrade: return((row.DescAvgGrade).ToString("0.0%")); case TrailResultColumnIds.AvgSpeed: return(UnitUtil.Speed.ToString(row.AvgSpeed, Controller.TrailController.Instance.ReferenceActivity, "")); case TrailResultColumnIds.FastestSpeed: return(UnitUtil.Speed.ToString(row.FastestSpeed, Controller.TrailController.Instance.ReferenceActivity, "")); case TrailResultColumnIds.AvgPace: return(UnitUtil.Pace.ToString(row.AvgSpeed, Controller.TrailController.Instance.ReferenceActivity, "")); case TrailResultColumnIds.FastestPace: return(UnitUtil.Pace.ToString(row.FastestSpeed, Controller.TrailController.Instance.ReferenceActivity, "")); case TrailResultColumnIds.AvgSpeedPace: return(UnitUtil.PaceOrSpeed.ToString(row.AvgSpeed, Controller.TrailController.Instance.ReferenceActivity, "")); case TrailResultColumnIds.FastestSpeedPace: return(UnitUtil.PaceOrSpeed.ToString(row.FastestSpeed, Controller.TrailController.Instance.ReferenceActivity, "")); case TrailResultColumnIds.PredictDistance: return(UnitUtil.Time.ToString(row.PredictDistance, "")); case TrailResultColumnIds.IdealTime: return(UnitUtil.Time.ToString(row.IdealTime, "")); case TrailResultColumnIds.GradeRunAdjustedTime: return(UnitUtil.Time.ToString(row.GradeRunAdjustedTime, "")); case TrailResultColumnIds.GradeRunAdjustedPace: return(UnitUtil.Pace.ToString(row.GradeRunAdjustedSpeed, Controller.TrailController.Instance.ReferenceActivity, "")); case TrailResultColumnIds.Diff: return(UnitUtil.Elevation.ToString(row.Diff, "")); case TrailResultColumnIds.AscendingSpeed_VAM: return(UnitUtil.Elevation.ToString(row.AscendingSpeed_VAM, "")); case TrailResultColumnIds.AveragePowerBalance: return((row.AveragePowerBalance / 100).ToString("0.0%")); case TrailResultColumnIds.AverageTemperature: return((row.AverageTemperature).ToString("0.0")); case TrailResultColumnIds.AverageGroundContactTime: return((row.AverageGroundContactTime).ToString("0")); case TrailResultColumnIds.AverageVerticalOscillation: return((row.AverageVerticalOscillation).ToString("0.0")); case TrailResultColumnIds.AverageSaturatedHemoglobin: return((row.AverageSaturatedHemoglobin / 100).ToString("0.0%")); case TrailResultColumnIds.AverageTotalHemoglobinConcentration: return((row.AverageTotalHemoglobinConcentration).ToString("0.0")); default: if (TrailResultColumns.IsLapField(column.Id)) { ILapInfo lap = row.LapInfo; if (lap != null) { //The column Id is faked to not clash with the internal ids TreeList.Column c = new TreeList.Column(TrailResultColumns.LapId(column.Id)); return(base.GetText(lap, c)); } //Not Splits trail return(null); } else if (TrailResultColumns.IsSwimLapField(column.Id)) { IPoolLengthInfo lap = row.PoolLengthInfo; if (lap != null) { //The column Id is faked to not clash with the internal ids TreeList.Column c = new TreeList.Column(TrailResultColumns.SwimLapId(column.Id)); return(base.GetText(lap, c)); } //Not Splits trail return(null); } else if (TrailResultColumns.IsActivityField(column.Id)) { if (row is ParentTrailResult) { if (row.Activity == null) { return(null); } if (column.Id == TrailResultColumnIds.MetaData_Source) { return(row.Activity.Metadata.Source); } return(base.GetText(row.Activity, column)); } return(null); } else { if (row.Activity == null) { return(null); } ICustomDataFieldDefinition cust = TrailResultColumns.CustomDef(column.Id); if (cust != null) { if (row is ParentTrailResult) { return(row.Activity.GetCustomDataValue(TrailResultColumns.CustomDef(column.Id)).ToString()); } return(null); } } System.Diagnostics.Debug.Assert(false, string.Format("No label info for id {0}", column.Id)); return(null); } }
public static int Compare(TrailResult x, TrailResult y) { if (x == null) { return(1); } if (y == null) { return(-1); } //Summary results are not supposed to be sorted, this should not be used if (x is SummaryTrailResult) { if ((x as SummaryTrailResult).IsTotal || !(y is SummaryTrailResult)) { return(1); } } if (y is SummaryTrailResult) { return(-1); } int result = 0; { //Paused results handling //Keep the paused result close to the unpaused result by using the normal resul //If two results to sort relate to the same normal result, use the start time to sort them TrailResult x0 = x; TrailResult y0 = y; if (x is PausedChildTrailResult) { x = (x as PausedChildTrailResult).RelatedChildResult; } if (y is PausedChildTrailResult) { y = (y as PausedChildTrailResult).RelatedChildResult; } if (x == y) { result = x0.StartTime.CompareTo(y0.StartTime); if (TrailsPlugin.Data.Settings.SummaryViewSortColumns.Count > 0 && TrailsPlugin.Data.Settings.SummaryViewSortColumns[0].Equals(TrailResultColumnIds.StartTime)) { //Switch order when sorting on start time only result *= (TrailsPlugin.Data.Settings.SummaryViewSortDirection == ListSortDirection.Ascending ? 1 : -1); } return(result); } } const string GradeRunAdjustedPace_Field = "GradeRunAdjustedSpeed"; foreach (string id0 in TrailsPlugin.Data.Settings.SummaryViewSortColumns) { bool reverseSortOrder = false; string id = id0; //Sort Pace and speed the same switch (id) { case TrailResultColumnIds.AvgPace: case TrailResultColumnIds.AvgSpeedPace: id = TrailResultColumnIds.AvgSpeed; break; case TrailResultColumnIds.FastestPace: case TrailResultColumnIds.FastestSpeedPace: id = TrailResultColumnIds.FastestSpeed; break; case TrailResultColumnIds.GradeRunAdjustedPace: reverseSortOrder = true; id = GradeRunAdjustedPace_Field; break; } //Limit "automatic" calculations in some situations if (TrailsPlugin.Controller.TrailController.Instance.AutomaticUpdate) { if (id == TrailResultColumnIds.GradeRunAdjustedTime || id == TrailResultColumnIds.PredictDistance || id == TrailResultColumnIds.IdealTime) { id = TrailResultColumnIds.Duration; } if (id == GradeRunAdjustedPace_Field) { id = TrailResultColumnIds.AvgPace; } } if (TrailResultColumnIds.ClimbFields.Contains(id) && (!x.ClimbCalculated || !y.ClimbCalculated)) { continue; } try { Boolean fieldFound = false; if (id == TrailResultColumnIds.MetaData_Source) { //Special handling, sub object fieldFound = true; if (x is ParentTrailResult && x.Activity != null && y.Activity != null) { result = x.Activity.Metadata.Source.CompareTo(y.Activity.Metadata.Source); } } { //Dont bother with reflection CompareTo, few types, just complicates TrailResult/Lap //If not parent result, there is no difference ICustomDataFieldDefinition cust = TrailResultColumns.CustomDef(id); if (cust != null) { fieldFound = true; if (x is ParentTrailResult && x.Activity != null && y.Activity != null) { object xoc = x.Activity.GetCustomDataValue(cust); object yoc = y.Activity.GetCustomDataValue(cust); if (xoc == null) { result = 1; } else if (yoc == null) { result = -1; } else if (cust.DataType.Id.Equals(new System.Guid("{6e0f7115-6aa3-49ea-a855-966ce17317a1}"))) { //numeric result = ((System.Double)xoc).CompareTo((System.Double)yoc); } else { //date or string result = ((string)xoc).CompareTo((string)yoc); } } } } if (!fieldFound) { //Use reflection to get values and compare routines //This is very slightly slower than hardcoded access. //Profiling on the simple Duration field, it takes 129 ms vs 150ms for 64000/57000(?) operations (Splits, 2400 activities, sorted twice) //(Total sort is 300/400 ms though) //An attempt to cache method gave 180ms 82000(?), this was removed //For most fields, this is not measurable, getting data is much slower object xo; object yo; if (IsLapField(id)) { //Note: Some lap/poolinfo fields are read using TrailResult properties id = LapId(id); xo = x.LapInfo; yo = y.LapInfo; } else if (IsSwimLapField(id)) { id = SwimLapId(id); xo = x.PoolLengthInfo; yo = y.PoolLengthInfo; } else if (IsActivityField(id)) { xo = x.Activity; yo = y.Activity; } else { xo = x; yo = y; } if (xo != null && yo != null) { //Only Properties, no fields searched PropertyInfo xf = xo.GetType().GetProperty(id); PropertyInfo yf = yo.GetType().GetProperty(id); if (xf == null) { Debug.Assert(false, string.Format("No property info for id {0} for x {1}", id, xo)); result = 1; } else if (yf == null) { Debug.Assert(false, string.Format("No property info for id {0} for y {1}", id, yo)); result = -1; } object xv = xf.GetValue(xo, null); object yv = yf.GetValue(yo, null); if (xv == null) { //Debug.Assert(false, string.Format("No value for id {0} for x {1}", id, xo)); if (yv == null) { result = 1; } } else if (yv == null) { Debug.Assert(false, string.Format("No value for id {0} for y {1}", id, yo)); if (xv == null) { result = -1; } } else { //Get the CompareTo method using reflection MethodInfo cmp = null; //Specialized version of generic (not applicable for .Net2) // from http://stackoverflow.com/questions/4035719/getmethod-for-generic-method foreach (MethodInfo methodInfo in xv.GetType().GetMember("CompareTo", MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance)) { // Check that the parameter counts and types match, // with 'loose' matching on generic parameters ParameterInfo[] parameterInfos = methodInfo.GetParameters(); if (parameterInfos.Length == 1) { if (parameterInfos[0].ParameterType.Equals(yv) || parameterInfos[0].ParameterType.Equals(typeof(object))) { cmp = methodInfo; break; } } } if (cmp != null) { result = (int)cmp.Invoke(xv, new object[1] { yv }); } else { Debug.Assert(false, string.Format("No CompareTo for id {0} for x {1}", id, xo)); } } } else if (xo == null) { result = 1; } else if (y == null) { result = -1; } } } catch (System.Exception e) { Debug.Assert(false, string.Format("Exception when finding properties for id {0} for x {1}, y {2}: {3}", id, x, y, e)); } if (result != 0) { if (reverseSortOrder) { result *= -1; } break; } } if (result == 0) { //Fallback sorting result = x.StartTime.CompareTo(y.StartTime); } result *= (TrailsPlugin.Data.Settings.SummaryViewSortDirection == ListSortDirection.Ascending ? 1 : -1); return(result); }