private static IEnumerable<Metric> GetDetailedMerics(XPathNavigator navigator, SiteConfigurationElement site)
        {
            var metrics = new List<Metric>();

            //Get the test id
            var node = navigator.SelectSingleNode("response/data/testId");
            if (node == null) return metrics;
            string testId = node.Value;

            //Get the test url
            node = navigator.SelectSingleNode("response/data/testUrl");
            if (node == null) return metrics;
            string testUrl = node.Value.Replace("http://", "");

            //Get the run date
            node = navigator.SelectSingleNode("response/data/run//date");
            if (node == null) return metrics;
            string dateTime = node.Value;
            var timeStamp = EpochToDateTime(dateTime, siteSection.AssumeLocalTimeZone);

            var request = WebRequest.Create(String.Format(CultureInfo.InvariantCulture, "{0}/result/{1}/{1}_{2}_requests.csv", siteSection.WebPagetestHost, testId, HttpUtility.UrlEncode(testUrl)));
            request.Timeout = 5000;
            var response = (HttpWebResponse)request.GetResponse();
            var stream = response.GetResponseStream();
            try
            {
                if (stream == null)
                {
                    return metrics;
                }

                using (var reader = new CsvReader(new StreamReader(stream)))
                {
                    stream = null;

                    foreach (var row in reader.GetRecords<DetailedWebpagetestRow>())
                    {
                        if (String.IsNullOrEmpty(row.Host))
                        {
                            continue;
                        }

                        //Extract the host to group by
                        string host = GetSubDomain("http://" + row.Host);
                        if (host == null)
                        {
                            host = row.Host;
                        }
                        host = host.Replace(".", "_");

                        //Get the run type
                        string runType = "firstView";
                        if (row.EventName.StartsWith("Cached"))
                        {
                            runType = "repeatView";
                        }

                        //TTFB - By Host
                        metrics.Add(new Metric
                        {
                            Key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}.byHost.{2}.ttfb", site.GraphiteKey, runType, host),
                            Timestamp = timeStamp,
                            Value = row.TimeToFirstByte
                        });
                        //TTFB - By MIME Type
                        if (!String.IsNullOrEmpty(row.ContentType))
                        {
                            metrics.Add(new Metric
                            {
                                Key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}.byType.{2}.ttfb", site.GraphiteKey, runType, row.ContentType),
                                Timestamp = timeStamp,
                                Value = row.TimeToFirstByte
                            });
                        }

                        //TTL - By Host
                        metrics.Add(new Metric
                        {
                            Key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}.byHost.{2}.ttl", site.GraphiteKey, runType, host),
                            Timestamp = timeStamp,
                            Value = row.TimeToLoad
                        });
                        //TTL - By MIME Type
                        if (!String.IsNullOrEmpty(row.ContentType))
                        {
                            metrics.Add(new Metric
                            {
                                Key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}.byType.{2}.ttl", site.GraphiteKey, runType, row.ContentType),
                                Timestamp = timeStamp,
                                Value = row.TimeToLoad
                            });
                        }

                        //Total bytes - By Host
                        metrics.Add(new Metric
                        {
                            Key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}.byHost.{2}.bytes.count", site.GraphiteKey, runType, host),
                            Timestamp = timeStamp,
                            Value = row.Bytes
                        });
                        //Total bytes - By MIME Type
                        if (!String.IsNullOrEmpty(row.ContentType))
                        {
                            metrics.Add(new Metric
                            {
                                Key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}.byType.{2}.bytes.count", site.GraphiteKey, runType, row.ContentType),
                                Timestamp = timeStamp,
                                Value = row.Bytes
                            });
                        }
                        //Avg bytes - By Host
                        metrics.Add(new Metric
                        {
                            Key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}.byHost.{2}.bytes.avg", site.GraphiteKey, runType, host),
                            Timestamp = timeStamp,
                            Value = row.Bytes
                        });
                        //Avg bytes - By MIME Type
                        if (!String.IsNullOrEmpty(row.ContentType))
                        {
                            metrics.Add(new Metric
                            {
                                Key = String.Format(CultureInfo.InvariantCulture, "{0}.{1}.byType.{2}.bytes.avg", site.GraphiteKey, runType, row.ContentType),
                                Timestamp = timeStamp,
                                Value = row.Bytes
                            });
                        }
                    }
                }
            }
            finally
            {
                if (stream != null)
                {
                    stream.Dispose();
                }
            }

            return metrics;
        }
        private IEnumerable<Metric> XmlToMetrics(SiteConfigurationElement site, IXPathNavigable result)
        {
            var metrics = new List<Metric>();
            if (result == null)
                throw new ArgumentNullException("result", "WebPagetest result was null");

            var navigator = result.CreateNavigator();

            //Check test is completed
            var code = navigator.SelectSingleNode("response/statusCode");
            if (code.Value == "200")
            {
                foreach (XPathNavigator runNavigator in navigator.Select("response/data/run"))
                {
                    var idNode = runNavigator.SelectSingleNode("id");
                    if (idNode == null) continue;

                    var run = site.AllowMultipleRuns ? "." + idNode.Value : String.Empty;

                    //firstView
                    DoView(runNavigator, "firstView", site, run, metrics, keysToKeep);

                    //repeatView
                    DoView(runNavigator, "repeatView", site, run, metrics, keysToKeep);

                    if (!site.AllowMultipleRuns)
                        break;
                }

                if (site.DetailedMetrics)
                {
                    metrics.AddRange(GetDetailedMerics(navigator, site));
                }
            }

            return metrics;
        }
        private static void DoView(XPathNavigator runNavigator, string view, SiteConfigurationElement site, string run, ICollection<Metric> metrics, ICollection<string> keysToKeep)
        {
            var viewNavigator = runNavigator.SelectSingleNode(view + "/results");
            if (viewNavigator != null)
            {
                var node = viewNavigator.SelectSingleNode("date");
                if (node == null) return;

                string dateTime = node.Value;
                var timeStamp = EpochToDateTime(dateTime, siteSection.AssumeLocalTimeZone);
                foreach (XPathNavigator metric in viewNavigator.SelectChildren(XPathNodeType.Element))
                {
                    if ((keysToKeep != null) && (!keysToKeep.Contains(metric.Name)))
                    {
                        continue;
                    }

                    int numericValue;
                    if (Int32.TryParse(metric.Value, out numericValue))
                    {
                        metrics.Add(new Metric
                                        {
                                            Key =
                                                String.Format(CultureInfo.InvariantCulture, "{0}.{1}{2}.{3}", site.GraphiteKey,
                                                              view, run, metric.Name),
                                            Timestamp = timeStamp,
                                            Value = numericValue
                                        });
                    }
                }
            }
        }