public SessionSummaryDto CalculateWorkoutSummary(SessionDto session, List<DetectedInterval> detectedIntervals, int unit)
        {
            var sessionSummaryDtoList = new List<SessionSummaryDto>();

            for (var w = 0; w < detectedIntervals.Count; w++)
            {
                var sessionDataSubsetDto = new SessionDataSubsetDto()
                {
                    MinimumSecond = (double)detectedIntervals[w].StartTime,
                    MaximumSecond = (double)detectedIntervals[w].FinishTime,
                    SessionId = session.Id,
                    Unit = unit
                };

                var sessionSummaryDto = GetSessionDataSubset(sessionDataSubsetDto);
                sessionSummaryDtoList.Add(sessionSummaryDto);
            }

            var totalCount = sessionSummaryDtoList.Count();

            // calculate speed
            var averageSpeed = Math.Round(sessionSummaryDtoList.Sum(x => x.AverageSpeed) / totalCount, 2, MidpointRounding.AwayFromZero);
            var maximumSpeed = sessionSummaryDtoList.MaxBy(s => s.MaximumSpeed).MaximumSpeed;

            // calculate distance
            var totalDistance = Math.Round(sessionSummaryDtoList.Sum(x => x.TotalDistance), 2, MidpointRounding.AwayFromZero);

            // calculate altitiude
            var averageAltitude = Math.Round(sessionSummaryDtoList.Sum(x => x.AverageAltitude) / totalCount, 2, MidpointRounding.AwayFromZero);
            var maximumAltitude = sessionSummaryDtoList.MaxBy(s => s.MaximumAltitude).MaximumAltitude;

            // calculate heart rate
            var averageHeartRate =  Math.Round(sessionSummaryDtoList.Sum(x => x.AverageHeartRate / totalCount), 2, MidpointRounding.AwayFromZero);
            var minimumHeartRate = sessionSummaryDtoList.MinBy(s => s.MinimumHeartRate).MinimumHeartRate;
            var maximumHeartRate = sessionSummaryDtoList.MinBy(s => s.MaximumHeartRate).MaximumHeartRate;

            // calculate power
            var averagePower =  Math.Round(sessionSummaryDtoList.Sum(x => x.AveragePower) / totalCount, 2, MidpointRounding.AwayFromZero);
            var maximumPower = sessionSummaryDtoList.MaxBy(s => s.MaximumPower).MaximumPower;

            // calculate cadence
            var averageCadence =  Math.Round(sessionSummaryDtoList.Sum(x => x.AverageCadence) / totalCount, 2, MidpointRounding.AwayFromZero);
            var maximumCadence = sessionSummaryDtoList.MaxBy(s => s.MaximumCadence).MaximumCadence;

            var workoutSummary = new SessionSummaryDto()
            {
                AverageAltitude = averageAltitude,
                MaximumAltitude = maximumAltitude,
                AverageHeartRate = averageHeartRate,
                MinimumHeartRate = minimumHeartRate,
                MaximumHeartRate = maximumHeartRate,
                AveragePower = averagePower,
                MaximumPower = maximumPower,
                AverageCadence = averageCadence,
                MaximumCadence = maximumCadence,
                AverageSpeed = averageSpeed,
                MaximumSpeed = maximumSpeed,
                TotalDistance = totalDistance,
                Date = session.Date,
                SessionId = session.Id
            };

            return workoutSummary;
        }
        public SessionSummaryDto GetSessionDataSubset(SessionDataSubsetDto sessionDataSubsetDto)
        {
            var minimumSeconds = sessionDataSubsetDto.MinimumSecond;
            var maximumSeconds = sessionDataSubsetDto.MaximumSecond;
            var totalTimeInHours = (maximumSeconds - minimumSeconds) / 3600;
            var requestedUnitIsMetric = sessionDataSubsetDto.Unit == 0;
            
            var session = _context.Sessions.Single(x => x.Id == sessionDataSubsetDto.SessionId);
            var sModeIsMetric = session.SMode.ToString("D9").IsMetric(); // pad int to 9 decimals if zero

            var sessionData = _context.SessionData.Where(x => x.SessionId == sessionDataSubsetDto.SessionId).ToList().OrderBy(x => x.Row)
                .Select(sd => new SessionDataDto()
                {
                    Id = sd.Id,
                    HeartRate = sd.HeartRate,
                    Speed = sd.Speed,
                    Cadence = sd.Cadence,
                    Altitude = sd.Altitude,
                    Power = sd.Power,
                    SessionId = sd.SessionId,
                    Date = DateFormat.CalculateSessionDataRowDate(session.Date, session.Interval, sd.Row)
                }).ToList();

            var filteredSessionData = sessionData.FilterListDates(session.Date, minimumSeconds, maximumSeconds); // filter sessions by min and max seconds
            var totalCount = filteredSessionData.Count();

            double maximumSpeed;
            double averageSpeed;
            double totalSpeed;
            double totalDistance;
            double averageAltitude;
            double maximumAltitude;
            double totalAltitude;

            if (requestedUnitIsMetric) // return metric values
            {
                if (sModeIsMetric)
                {
                    // calculate speed - divided speed by 10 as speed is *10 in file
                    totalSpeed = filteredSessionData.Sum(s => s.Speed);
                    averageSpeed = Math.Round((totalSpeed / 10) / totalCount, 2, MidpointRounding.AwayFromZero);
                    maximumSpeed = Math.Round(filteredSessionData.MaxBy(s => s.Speed).Speed / 10, 2, MidpointRounding.AwayFromZero);

                    // calculate distance
                    totalDistance = Math.Round(averageSpeed * totalTimeInHours, 2, MidpointRounding.AwayFromZero);

                    // calculate altitiude
                    totalAltitude = filteredSessionData.Sum(s => s.Altitude / 10);
                    averageAltitude = Math.Round(totalAltitude / totalCount, 2, MidpointRounding.AwayFromZero);
                    maximumAltitude = Math.Round(filteredSessionData.MaxBy(s => s.Altitude).Altitude / 10, 2, MidpointRounding.AwayFromZero);
                }
                else
                {
                    // calculate speed - divided speed by 10 as speed is *10 in file - converted kilometres to miles
                    totalSpeed = filteredSessionData.Sum(s => s.Speed);
                    averageSpeed = ((totalSpeed / 10) / totalCount).ConvertToKilometres();
                    maximumSpeed = (filteredSessionData.MaxBy(s => s.Speed).Speed / 10).ConvertToKilometres();

                    // calculate distance
                    totalDistance = ((totalSpeed / 10) / totalCount * totalTimeInHours).ConvertToKilometres();

                    // calculate altitiude - convert metres to feet
                    totalAltitude = filteredSessionData.Sum(s => s.Altitude / 10);
                    averageAltitude = (totalAltitude / totalCount).ConvertToMetres();
                    maximumAltitude = (filteredSessionData.MaxBy(s => s.Altitude).Altitude / 10).ConvertToMetres();
                }
            }
            else // return imperial values
            {
                if (sModeIsMetric)
                {
                    // calculate speed - divided speed by 10 as speed is *10 in file - converted kilometres to miles
                    totalSpeed = filteredSessionData.Sum(s => s.Speed);
                    averageSpeed = ((totalSpeed / 10) / totalCount).ConvertToMiles();
                    maximumSpeed = (filteredSessionData.MaxBy(s => s.Speed).Speed / 10).ConvertToMiles();

                    // calculate distance
                    totalDistance = ((totalSpeed / 10) / totalCount * totalTimeInHours).ConvertToMiles();

                    // calculate altitiude - convert metres to feet
                    totalAltitude = filteredSessionData.Sum(s => s.Altitude / 10);
                    averageAltitude = (totalAltitude / totalCount).ConvertToFeet();
                    maximumAltitude = (filteredSessionData.MaxBy(s => s.Altitude).Altitude / 10).ConvertToFeet();
                }
                else
                {
                    // calculate speed - divided speed by 10 as speed is *10 in file
                    totalSpeed = filteredSessionData.Sum(s => s.Speed);
                    averageSpeed = Math.Round((totalSpeed / 10) / totalCount, 2, MidpointRounding.AwayFromZero);
                    maximumSpeed = Math.Round(
                    filteredSessionData.MaxBy(s => s.Speed).Speed / 10, 2, MidpointRounding.AwayFromZero);

                    // calculate distance
                    totalDistance = Math.Round(averageSpeed * totalTimeInHours, 2, MidpointRounding.AwayFromZero);

                    // calculate altitiude
                    totalAltitude = filteredSessionData.Sum(s => s.Altitude / 10);
                    averageAltitude = Math.Round(totalAltitude / totalCount, 2, MidpointRounding.AwayFromZero);
                    maximumAltitude = Math.Round(filteredSessionData.MaxBy(s => s.Altitude).Altitude / 10, 2, MidpointRounding.AwayFromZero);
                }
            }

            // calculate heart rate
            var totalHeartRate = filteredSessionData.Sum(s => s.HeartRate);
            var averageHeartRate =  Math.Round(totalHeartRate / totalCount, 2, MidpointRounding.AwayFromZero);
            var minimumHeartRate = filteredSessionData.MinBy(s => s.HeartRate).HeartRate;
            var maximumHeartRate = filteredSessionData.MaxBy(s => s.HeartRate).HeartRate;

            // calculate power
            var totalPower = filteredSessionData.Sum(s => s.Power);
            var averagePower =  Math.Round(totalPower / totalCount, 2, MidpointRounding.AwayFromZero);
            var maximumPower =  Math.Round(filteredSessionData.MaxBy(s => s.Power).Power, 2, MidpointRounding.AwayFromZero);

            // calculate cadence
            var totalCadence = filteredSessionData.Sum(s => s.Cadence);
            var averageCadence = Math.Round(totalCadence / totalCount, 2, MidpointRounding.AwayFromZero);
            var maximumCadence = Math.Round(filteredSessionData.MaxBy(s => s.Cadence).Cadence, 2, MidpointRounding.AwayFromZero);

            // calculate normalized power
            var filteredSessionDto = new SessionDto()
            {
                Interval = session.Interval,
                SessionData = filteredSessionData
            };
            var normalizedPower = filteredSessionDto.CalculateNormalizedPower();

            // calculate intensity factor
            var functionalThresholdPower = _context.Athletes.Single(a => a.Id == session.AthleteId).FunctionalThresholdPower;
            var intensityFactor = normalizedPower.CalculateIntensityFactor(functionalThresholdPower);

            // calculate training stress score
            var sessionTimeInSeconds = filteredSessionData.Count * filteredSessionDto.Interval; //session.Length.TimeOfDay.TotalSeconds;
            double trainingStressScore = 0;
            string trainingStressScoreStatus = "";

            if (normalizedPower != 0)
            {
                trainingStressScore = Metrics.CalculateTrainingStressScore(sessionTimeInSeconds, normalizedPower, intensityFactor, functionalThresholdPower);
                trainingStressScoreStatus = Metrics.TrainingStressScoreStatus(trainingStressScore);
            }

            var sessionSummaryDto = new SessionSummaryDto()
            {
                AverageAltitude = averageAltitude,
                MaximumAltitude = maximumAltitude,
                AverageHeartRate = averageHeartRate,
                MinimumHeartRate = minimumHeartRate,
                MaximumHeartRate = maximumHeartRate,
                AveragePower = averagePower,
                MaximumPower = maximumPower,
                AverageCadence = averageCadence,
                MaximumCadence = maximumCadence,
                AverageSpeed = averageSpeed,
                MaximumSpeed = maximumSpeed,
                TotalDistance = totalDistance,
                NormalizedPower = normalizedPower,
                IntensityFactor = intensityFactor,
                TrainingStressScore = trainingStressScore,
                TrainingStressScoreStatus = trainingStressScoreStatus,
                Date = session.Date,
                SessionId = session.Id
            };

            return sessionSummaryDto;
        }