public IGraph GetGraph(string sprintId)
        {
            var sprintFilter = " Sprint: " + sprintId;
            var lineDefinitions = new Dictionary<string, string>
                {
                    {"#{Business Systems} -JDEdwards -Construction -Vendor" + sprintFilter, "BS.NET"},
                    {"#{Business Systems} #{JDEdwards}" + sprintFilter, "JDE"},
                    {"#{Marketing Systems}" + sprintFilter, "Marketing"},
                    {"#{Construction} #{Vendor}" + sprintFilter, "Headspring"}
                };

            var fields = new[] {"id", "resolved", "Estimation", "summary", "type"};

            var ignoreTypes = new[] {"Feature", "Epic", "User Story"};

            var sprint = GetSprint(sprintId);

            var results = (MultiSearchResult) _client.QueryIssues(lineDefinitions.Keys.ToArray(), fields, 2000, 1);

            var issues = results.SearchResult
                                .SelectMany(sr => sr.Issues,
                                            (sr, issue) => new Issue(lineDefinitions[sr.Search], issue))
                                .ToArray();

            var graph = new Graph();

            var ideal = graph.GenerateIdealLine(sprint.Start, sprint.End);

            foreach (var group in issues.GroupBy(i => i.LineLabel))
            {
                var line = graph.AddLine(group.Key);
                FillLine(line, sprint, group.ToArray(), ignoreTypes);
            }

            ideal = graph.Lines.First();

            if (!ideal.Begin.HasValue || !ideal.End.HasValue)
                return graph.Normalize();

            if (DateTime.UtcNow < ideal.Begin.Value || DateTime.UtcNow > ideal.End.Value)
                return graph.Normalize();

            foreach (var line in graph.Lines.Except(new[] {ideal}).Cast<Line>())
            {
                if (!line.Points.Any())
                    continue;

                if (line.End.HasValue && line.End.Value > ideal.End.Value)
                    continue;

                if (line.Min.HasValue && line.Min.Value < 0.01)
                    continue;

                // Figure out how far to project this line...
                var lastTS = DateTime.UtcNow;
                if (lastTS > ideal.End.Value)
                    lastTS = ideal.End.Value;
                if (line.End.Value > lastTS)
                    lastTS = line.End.Value;
                var lastValue = line.Points.Last().Value;

                // Project 0 progress through this moment
                var point = line.AddPoint(lastValue, lastTS);
                point.IsProjection = true;
                point.OriginalValue = (float) Math.Round(lastValue);

                if (lastTS < ideal.End.Value)
                {
                    // Project average progress through the end of the sprint
                    var elapsed = line.End.Value.Subtract(line.Begin.Value).Ticks;
                    var completed = line.Min.Value - line.Max.Value;

                    if (Math.Abs(completed - 0) < 0.001)
                        continue;

                    var rate = completed/elapsed;

                    // y = mx + b;
                    // 0 = rate * x + max;
                    // -max = rate * x;
                    // -max / rate = x;
                    var estimatedCompletion = line.Begin.Value.AddTicks(Convert.ToInt64(-line.Max.Value/rate));

                    // y = mx + b
                    // y = rate * sprintTicks + max
                    var sprintTicks = ideal.End.Value.Subtract(line.Begin.Value).Ticks;

                    var estimatedPointsRemaining = rate*sprintTicks + line.Max.Value;

                    point = estimatedCompletion > ideal.End.Value
                                ? line.AddPoint(estimatedPointsRemaining, ideal.End.Value)
                                : line.AddPoint(0, estimatedCompletion);

                    point.OriginalValue = (float) Math.Round(point.Value);

                    point.Label = "Points remaining at sprint completion: " + Convert.ToInt64(estimatedPointsRemaining);
                    point.Description = "Estimated completion of points: " + estimatedCompletion.ToString("f");

                    point.Label = string.Format("{0} by {1} points",
                                                estimatedPointsRemaining >= 0 ? "Over" : "Under",
                                                Convert.ToInt64(Math.Abs(estimatedPointsRemaining)));
                    point.IsProjection = true;
                }
            }
            return graph.Normalize();
        }