private void ComputeLeadTimeHistogram() { var maxLeadTime = DoneIssues.Length > 0 ? DoneIssues.Max(_ => _.LeadTime) : 0; var buckets = Enumerable.Range(0, maxLeadTime + 1).Paged(3).Select(_ => new { Start = _.First(), End = _.Last() }).ToList(); var leadHistogram = buckets.Select(bucket => new { LeadTime = $"{bucket.Start}-{bucket.End}", Qty = DoneIssues.Where(ticket => ticket.LeadTime >= bucket.Start && ticket.LeadTime <= bucket.End).Count(ticket => !ticket.IssueType.IsDefect(Config)), QtyDefects = DoneIssues.Where(ticket => ticket.LeadTime >= bucket.Start && ticket.LeadTime <= bucket.End).Count(ticket => ticket.IssueType.IsDefect(Config)), }) .ToList(); { var chart = new Chart(); var chartArea = new ChartArea(); chart.ChartAreas.Add(chartArea); chart.Legends.Add("legend").Docking = Docking.Bottom; var series = new Series { ChartType = SeriesChartType.Column }; series.Points.DataBindXY(leadHistogram.Select(_ => _.LeadTime).ToArray(), leadHistogram.Select(_ => _.Qty).ToArray()); series.Legend = "legend"; series.LegendText = "Tasks"; chart.Series.Add(series); series = new Series { ChartType = SeriesChartType.Column }; series.Points.DataBindXY(leadHistogram.Select(_ => _.LeadTime).ToArray(), leadHistogram.Select(_ => _.QtyDefects).ToArray()); series.Legend = "legend"; series.LegendText = "Defects"; series.Color = Color.Red; chart.Series.Add(series); chartArea.AxisX.Interval = 1; chartArea.AxisX.LabelStyle.Angle = -65; chartArea.AxisY.IntervalAutoMode = IntervalAutoMode.VariableCount; LeadTimeHistogramChart = chart; } }
private void ComputeFlowEfficiency() { var flowEfficiency = DoneIssues .GroupBy(ticket => ticket.Done.YearWeek()) .Where(g => g.Sum(_ => _.LeadTime) > 0) .Select(g => new { Week = g.Key, Tickets = g.Count(), AverageLeadTime = (int)Math.Round(g.Average(_ => _.LeadTime), 0), FlowEfficiency = Math.Round(g.Sum(_ => _.TouchTime) / (double)g.Sum(_ => _.LeadTime) * 100D, 1) }) .OrderBy(_ => _.Week) .ToList(); // Chart { var chart = new Chart(); var chartArea = new ChartArea(); chart.ChartAreas.Add(chartArea); var series = new Series { ChartType = SeriesChartType.Line }; series.Points.DataBindXY(flowEfficiency.Select(_ => _.Week).ToArray(), flowEfficiency.Select(_ => _.FlowEfficiency).ToArray()); chart.Series.Add(series); chartArea.AxisX.Interval = 1; chartArea.AxisX.LabelStyle.Angle = -65; chartArea.AxisY.Title = "Flow efficiency %"; chartArea.AxisY.Maximum = 100; var average = flowEfficiency.Count > 0 ? flowEfficiency.Average(_ => _.FlowEfficiency) : 0; chartArea.AxisY.StripLines.Add(CreateStripLine(average, Color.Blue)); FlowEfficiencyChart = chart; } }
private void ComputeControlChart() { var controlChart = DoneIssues.OrderBy(ticket => ticket.Done) .Select(ticket => new { ticket.IssueKey, ticket.IssueType, Done = ticket.Done.ToString("yyyy-MM-dd"), ticket.LeadTime }) .ToList(); { var chart = new Chart(); var chartArea = new ChartArea(); var start = DoneIssues.Length > 0 ? DoneIssues.Min(_ => _.Done) : StartDate; var max = DoneIssues.Length > 0 ? DoneIssues.Max(_ => _.Done) : EndDate; var numberOfDays = (int)(max - start).TotalDays + 1; var xindex = Enumerable.Range(0, numberOfDays).Select(i => start.AddDays(i).ToString("yyyy-MM-dd")) .Distinct().OrderBy(_ => _).ToList(); chart.ChartAreas.Add(chartArea); chart.Legends.Add("legend").Docking = Docking.Bottom; { var tickets = controlChart.Where(_ => !_.IssueType.IsDefect(Config)); var series = new Series(); series.ChartType = SeriesChartType.Point; foreach (var g in tickets) { series.Points.Add(new DataPoint() { AxisLabel = g.Done, XValue = xindex.IndexOf(g.Done), YValues = new double[] { g.LeadTime } }); } series.CustomProperties = "IsXAxisQuantitative=True"; series.Legend = "legend"; series.LegendText = "Tasks"; series.Color = Color.Blue; chart.Series.Add(series); } { var tickets = controlChart.Where(_ => _.IssueType.IsDefect(Config)); var series = new Series { ChartType = SeriesChartType.Point }; foreach (var g in tickets) { series.Points.Add(new DataPoint() { AxisLabel = g.Done, XValue = xindex.IndexOf(g.Done), YValues = new double[] { g.LeadTime } }); } series.CustomProperties = "IsXAxisQuantitative=True"; series.Legend = "legend"; series.LegendText = "Defects"; series.Color = Color.Red; chart.Series.Add(series); } var avg = controlChart.Count > 0 ? controlChart.Average(_ => _.LeadTime) : 0; var p95 = controlChart.Count > 0 ? controlChart.Percentile(95, _ => _.LeadTime) : 0; var p90 = controlChart.Count > 0 ? controlChart.Percentile(90, _ => _.LeadTime) : 0; chartArea.AxisY.StripLines.Add(CreateStripLine(avg, Color.Blue, $"Average ({avg:0})")); chartArea.AxisY.StripLines.Add(CreateStripLine(p95, Color.Violet, $"95th percentile ({p95:0})")); chartArea.AxisY.StripLines.Add(CreateStripLine(p90, Color.Brown, $"90th percentile ({p90:0})")); chartArea.AxisX.Interval = 2; chartArea.AxisX.LabelStyle.Angle = -65; chartArea.AxisY.IntervalAutoMode = IntervalAutoMode.VariableCount; chartArea.AxisY.Title = "Lead time"; LeadTimeControlChart = chart; } }
private void ComputeThroughput() { var throughput = DoneIssues .GroupBy(ticket => ticket.Done.YearWeek()) .Select(g => new { Week = g.Key, Throughput = g.Count(), Value = g.Count(_ => !_.IssueType.IsDefect(Config)), Defects = g.Count(_ => _.IssueType.IsDefect(Config)), }) .Select(g => new { g.Week, g.Throughput, g.Value, g.Defects, FailureDemand = Math.Round((g.Defects / (float)g.Throughput) * 100, 1) }) .OrderBy(_ => _.Week) .ToList(); var maxThroughput = throughput.Count > 0 ? throughput.Max(_ => _.Throughput) : 0; var buckets = Enumerable.Range(0, maxThroughput + 1).Paged(4) .Select(_ => new { Start = _.First(), End = _.Last() }).ToList(); var throughputHistogram = buckets.Select(bucket => new { WeeklyThroughput = $"{bucket.Start}-{bucket.End}", NumberOfWeeks = throughput.Count(_ => _.Throughput >= bucket.Start && _.Throughput <= bucket.End), }) .ToList(); { var chart = new Chart(); var chartArea = new ChartArea(); chart.ChartAreas.Add(chartArea); var series = new Series { ChartType = SeriesChartType.Column }; series.Points.DataBindXY(throughputHistogram.Select(_ => _.WeeklyThroughput).ToArray(), throughputHistogram.Select(_ => _.NumberOfWeeks).ToArray()); chart.Series.Add(series); chartArea.AxisY.Title = "Quantity of weeks"; chartArea.AxisX.Title = "Throughput range"; chartArea.AxisX.Interval = 1; chartArea.AxisX.LabelStyle.Angle = -65; WeeklyThroughputHistogramChart = chart; } { var chart = new Chart(); var chartArea = new ChartArea(); chart.ChartAreas.Add(chartArea); chart.Legends.Add("legend").Docking = Docking.Bottom; { var series = new Series { ChartType = SeriesChartType.Line }; series.Points.DataBindXY(throughput.Select(_ => _.Week).ToArray(), throughput.Select(_ => _.Throughput).ToArray()); series.Legend = "legend"; series.LegendText = "Throughput (tasks + defects)"; series.BorderWidth = 3; chart.Series.Add(series); } { var series = new Series { ChartType = SeriesChartType.Line }; series.Points.DataBindXY(throughput.Select(_ => _.Week).ToArray(), throughput.Select(_ => _.Defects).ToArray()); series.Legend = "legend"; series.LegendText = "Defects"; series.Color = Color.Red; chart.Series.Add(series); } chartArea.AxisX.Interval = 1; chartArea.AxisX.LabelStyle.Angle = -65; chartArea.AxisY.IntervalAutoMode = IntervalAutoMode.VariableCount; var average = throughput.Count > 0 ? throughput.Average(_ => _.Throughput) : 0; chartArea.AxisY.StripLines.Add(CreateStripLine(average, Color.Blue)); WeeklyThroughputChart = chart; } }