public async Task <DeploymentFrequencyModel> GetAzureDevOpsDeploymentFrequency(bool getSampleData, string patToken, TableStorageAuth tableStorageAuth, string organization, string project, string branch, string buildName, int numberOfDays, int maxNumberOfItems, bool useCache) { ListUtility <Build> utility = new ListUtility <Build>(); DeploymentFrequency deploymentFrequency = new DeploymentFrequency(); if (getSampleData == false) { //Get a list of builds BuildsDA buildsDA = new BuildsDA(); List <AzureDevOpsBuild> azureDevOpsBuilds = await buildsDA.GetAzureDevOpsBuilds(patToken, tableStorageAuth, organization, project, buildName, useCache); if (azureDevOpsBuilds != null) { //Translate the Azure DevOps build to a generic build object List <Build> builds = new List <Build>(); foreach (AzureDevOpsBuild item in azureDevOpsBuilds) { //Only return completed builds on the target branch, within the targeted date range if (item.status == "completed" && item.sourceBranch == branch && item.queueTime > DateTime.Now.AddDays(-numberOfDays)) { builds.Add( new Build { Id = item.id, Branch = item.sourceBranch, BuildNumber = item.buildNumber, StartTime = item.queueTime, EndTime = item.finishTime, BuildDurationPercent = item.buildDurationPercent, Status = item.status, Url = item.url } ); } } //Get the total builds used in the calculation int buildTotal = builds.Count; //then build the calcuation, loading the dates into a date array List <KeyValuePair <DateTime, DateTime> > dateList = new List <KeyValuePair <DateTime, DateTime> >(); foreach (Build item in builds) { KeyValuePair <DateTime, DateTime> newItem = new KeyValuePair <DateTime, DateTime>(item.StartTime, item.EndTime); dateList.Add(newItem); } //then build the calcuation, loading the dates into a date array float deploymentsPerDay; deploymentsPerDay = deploymentFrequency.ProcessDeploymentFrequency(dateList, numberOfDays); //Filter the results to return the last n (maxNumberOfItems), to return to the UI builds = utility.GetLastNItems(builds, maxNumberOfItems); //Find the max build duration float maxBuildDuration = 0f; foreach (Build item in builds) { if (item.BuildDuration > maxBuildDuration) { maxBuildDuration = item.BuildDuration; } } //Calculate the percent scaling foreach (Build item in builds) { float interiumResult = ((item.BuildDuration / maxBuildDuration) * 100f); item.BuildDurationPercent = Scaling.ScaleNumberToRange(interiumResult, 0, 100, 20, 100); } //Return the completed model DeploymentFrequencyModel model = new DeploymentFrequencyModel { TargetDevOpsPlatform = DevOpsPlatform.AzureDevOps, DeploymentName = buildName, BuildList = builds, DeploymentsPerDayMetric = deploymentsPerDay, DeploymentsPerDayMetricDescription = deploymentFrequency.GetDeploymentFrequencyRating(deploymentsPerDay), NumberOfDays = numberOfDays, MaxNumberOfItems = builds.Count, TotalItems = buildTotal }; return(model); } else { return(null); } } else { //Get sample data List <Build> builds = utility.GetLastNItems(GetSampleAzureDevOpsBuilds(), maxNumberOfItems); DeploymentFrequencyModel model = new DeploymentFrequencyModel { TargetDevOpsPlatform = DevOpsPlatform.AzureDevOps, DeploymentName = buildName, BuildList = builds, DeploymentsPerDayMetric = 10f, DeploymentsPerDayMetricDescription = "Elite", NumberOfDays = numberOfDays, MaxNumberOfItems = builds.Count, TotalItems = builds.Count }; return(model); } }
public async Task <DeploymentFrequencyModel> GetGitHubDeploymentFrequency(bool getSampleData, string clientId, string clientSecret, TableStorageAuth tableStorageAuth, string owner, string repo, string branch, string workflowName, string workflowId, int numberOfDays, int maxNumberOfItems, bool useCache) { ListUtility <Build> utility = new ListUtility <Build>(); DeploymentFrequency deploymentFrequency = new DeploymentFrequency(); if (getSampleData == false) { //Get a list of builds BuildsDA buildsDA = new BuildsDA(); List <GitHubActionsRun> gitHubRuns = await buildsDA.GetGitHubActionRuns(clientId, clientSecret, tableStorageAuth, owner, repo, workflowName, workflowId, useCache); if (gitHubRuns != null) { //Translate the GitHub build to a generic build object List <Build> builds = new List <Build>(); foreach (GitHubActionsRun item in gitHubRuns) { //Only return completed builds on the target branch, within the targeted date range if (item.status == "completed" && item.head_branch == branch && item.created_at > DateTime.Now.AddDays(-numberOfDays)) { builds.Add( new Build { Id = item.run_number, Branch = item.head_branch, BuildNumber = item.run_number, StartTime = item.created_at, EndTime = item.updated_at, BuildDurationPercent = item.buildDurationPercent, Status = item.status, Url = item.html_url } ); } } //Get the total builds used in the calculation int buildTotal = builds.Count; //then build the calcuation, loading the dates into a date array List <KeyValuePair <DateTime, DateTime> > dateList = new List <KeyValuePair <DateTime, DateTime> >(); foreach (Build item in builds) { KeyValuePair <DateTime, DateTime> newItem = new KeyValuePair <DateTime, DateTime>(item.StartTime, item.EndTime); dateList.Add(newItem); } //then build the calcuation, loading the dates into a date array float deploymentsPerDay; deploymentsPerDay = deploymentFrequency.ProcessDeploymentFrequency(dateList, numberOfDays); //Filter the results to return the last n (maxNumberOfItems), to return to the UI builds = utility.GetLastNItems(builds, maxNumberOfItems); //Find the max build duration float maxBuildDuration = 0f; foreach (Build item in builds) { if (item.BuildDuration > maxBuildDuration) { maxBuildDuration = item.BuildDuration; } } //Calculate the percent scaling foreach (Build item in builds) { float interiumResult = ((item.BuildDuration / maxBuildDuration) * 100f); item.BuildDurationPercent = Scaling.ScaleNumberToRange(interiumResult, 0, 100, 20, 100); } //Return the completed model DeploymentFrequencyModel model = new DeploymentFrequencyModel { TargetDevOpsPlatform = DevOpsPlatform.GitHub, DeploymentName = workflowName, BuildList = builds, DeploymentsPerDayMetric = deploymentsPerDay, DeploymentsPerDayMetricDescription = deploymentFrequency.GetDeploymentFrequencyRating(deploymentsPerDay), NumberOfDays = numberOfDays, MaxNumberOfItems = builds.Count, TotalItems = buildTotal }; return(model); } else { return(null); } } else { List <Build> builds = utility.GetLastNItems(GetSampleGitHubBuilds(), maxNumberOfItems); DeploymentFrequencyModel model = new DeploymentFrequencyModel { TargetDevOpsPlatform = DevOpsPlatform.GitHub, DeploymentName = workflowName, BuildList = builds, DeploymentsPerDayMetric = 10f, DeploymentsPerDayMetricDescription = "Elite", NumberOfDays = numberOfDays, MaxNumberOfItems = builds.Count, TotalItems = builds.Count }; return(model); } }
public ChangeFailureRateModel GetChangeFailureRate(bool getSampleData, TableStorageAuth tableStorageAuth, DevOpsPlatform targetDevOpsPlatform, string organization_owner, string project_repo, string branch, string buildName_workflowName, int numberOfDays, int maxNumberOfItems) { ListUtility <ChangeFailureRateBuild> utility = new ListUtility <ChangeFailureRateBuild>(); ChangeFailureRate changeFailureRate = new ChangeFailureRate(); if (getSampleData == false) { //Gets a list of change failure rate builds from Azure storage AzureTableStorageDA daTableStorage = new AzureTableStorageDA(); Newtonsoft.Json.Linq.JArray list = daTableStorage.GetTableStorageItems(tableStorageAuth, tableStorageAuth.TableChangeFailureRate, daTableStorage.CreateBuildWorkflowPartitionKey(organization_owner, project_repo, buildName_workflowName)); List <ChangeFailureRateBuild> initialBuilds = JsonConvert.DeserializeObject <List <ChangeFailureRateBuild> >(list.ToString()); //Build the date list and then generate the change failure rate metric List <ChangeFailureRateBuild> builds = new List <ChangeFailureRateBuild>(); List <KeyValuePair <DateTime, bool> > dateList = new List <KeyValuePair <DateTime, bool> >(); float maxBuildDuration = 0f; foreach (ChangeFailureRateBuild item in initialBuilds) { if (item.Branch == branch && item.StartTime > DateTime.Now.AddDays(-numberOfDays)) { //Special branch for Azure DevOps to construct the Url to each build if (targetDevOpsPlatform == DevOpsPlatform.AzureDevOps) { item.Url = $"https://dev.azure.com/{organization_owner}/{project_repo}/_build/results?buildId={item.Id}&view=results"; } builds.Add(item); } } //then build the calcuation foreach (ChangeFailureRateBuild item in builds) { KeyValuePair <DateTime, bool> newItem = new KeyValuePair <DateTime, bool>(item.StartTime, item.DeploymentWasSuccessful); dateList.Add(newItem); } //calculate the metric on all of the results float changeFailureRateMetric = changeFailureRate.ProcessChangeFailureRate(dateList, numberOfDays); //Filter the results to return the last n (maxNumberOfItems) List <ChangeFailureRateBuild> uiBuilds = utility.GetLastNItems(builds, maxNumberOfItems); foreach (ChangeFailureRateBuild item in uiBuilds) { if (item.BuildDuration > maxBuildDuration) { maxBuildDuration = item.BuildDuration; } } //We need to do some post processing and loop over the list a couple times to find the max build duration, construct a usable url, and calculate a build duration percentage foreach (ChangeFailureRateBuild item in uiBuilds) { float interiumResult = ((item.BuildDuration / maxBuildDuration) * 100f); item.BuildDurationPercent = Scaling.ScaleNumberToRange(interiumResult, 0, 100, 20, 100); } ChangeFailureRateModel model = new ChangeFailureRateModel { TargetDevOpsPlatform = targetDevOpsPlatform, DeploymentName = buildName_workflowName, ChangeFailureRateBuildList = uiBuilds, ChangeFailureRateMetric = changeFailureRateMetric, ChangeFailureRateMetricDescription = changeFailureRate.GetChangeFailureRateRating(changeFailureRateMetric), NumberOfDays = numberOfDays, MaxNumberOfItems = uiBuilds.Count, TotalItems = builds.Count }; return(model); } else { //Get sample data List <ChangeFailureRateBuild> sampleBuilds = utility.GetLastNItems(GetSampleBuilds(), maxNumberOfItems); ChangeFailureRateModel model = new ChangeFailureRateModel { TargetDevOpsPlatform = targetDevOpsPlatform, DeploymentName = buildName_workflowName, ChangeFailureRateBuildList = sampleBuilds, ChangeFailureRateMetric = 2f / 10f, ChangeFailureRateMetricDescription = changeFailureRate.GetChangeFailureRateRating(2f / 10f), NumberOfDays = numberOfDays, MaxNumberOfItems = sampleBuilds.Count, TotalItems = sampleBuilds.Count }; return(model); } }
public MeanTimeToRestoreModel GetAzureMeanTimeToRestore(bool getSampleData, TableStorageAuth tableStorageAuth, DevOpsPlatform targetDevOpsPlatform, string resourceGroup, int numberOfDays, int maxNumberOfItems) { ListUtility <MeanTimeToRestoreEvent> utility = new ListUtility <MeanTimeToRestoreEvent>(); if (getSampleData == false) { //Pull the events from the table storage AzureTableStorageDA daTableStorage = new AzureTableStorageDA(); Newtonsoft.Json.Linq.JArray list = daTableStorage.GetTableStorageItems(tableStorageAuth, tableStorageAuth.TableMTTR, resourceGroup); List <AzureAlert> alerts = new List <AzureAlert>(); foreach (JToken item in list) { alerts.Add( new AzureAlert { name = item["data"]["context"]["name"].ToString(), resourceGroupName = item["data"]["context"]["resourceGroupName"].ToString(), resourceName = item["data"]["context"]["resourceName"].ToString(), status = item["data"]["status"].ToString(), timestamp = DateTime.Parse(item["data"]["context"]["timestamp"].ToString()) }); } //sort the events by timestamp alerts = alerts.OrderBy(o => o.timestamp).ToList(); //Compile the events, looking for pairs, using the ordered data, and name, resource group name and resource name List <MeanTimeToRestoreEvent> events = new List <MeanTimeToRestoreEvent>(); //Loop through first finding the activated alerts int i = 0; List <AzureAlert> startingAlerts = alerts.Where(o => o.status == "Activated").ToList(); foreach (AzureAlert item in startingAlerts) { if (item.timestamp > DateTime.Now.AddDays(-numberOfDays)) { i++; MeanTimeToRestoreEvent newEvent = new MeanTimeToRestoreEvent { Name = item.name, Resource = item.resourceName, ResourceGroup = item.resourceGroupName, StartTime = item.timestamp, Status = "inProgress", ItemOrder = i }; events.Add(newEvent); } } //Now loop through again, looking for the deactivated matching pair float maxEventDuration = 0; List <AzureAlert> endingAlerts = alerts.Where(o => o.status == "Deactivated").ToList(); foreach (MeanTimeToRestoreEvent item in events) { //Search for the next matching deactivated alert int foundItemIndex = -1; for (int j = 0; j <= endingAlerts.Count - 1; j++) { if (endingAlerts[j].name == item.Name && endingAlerts[j].resourceName == item.Resource && endingAlerts[j].resourceGroupName == item.ResourceGroup && endingAlerts[j].timestamp > item.StartTime) { item.EndTime = endingAlerts[j].timestamp; item.Status = "completed"; foundItemIndex = j; break; } } if (foundItemIndex >= 0) { //Remove the found item from the list, so we don't use it again. endingAlerts.RemoveAt(foundItemIndex); if (item.MTTRDurationInHours > maxEventDuration) { maxEventDuration = item.MTTRDurationInHours; } } } //Calculate the MTTR metric MeanTimeToRestore mttr = new MeanTimeToRestore(); float averageMTTR = CalculateMTTRDuration(events); //Filter and sort the final list (May not be needed due to the initial sort on the starting alerts) List <MeanTimeToRestoreEvent> uiEvents = utility.GetLastNItems(events, maxNumberOfItems); uiEvents = uiEvents.OrderBy(o => o.StartTime).ToList(); //Finally, process the percent calculation foreach (MeanTimeToRestoreEvent item in uiEvents) { float interiumResult = ((item.MTTRDurationInHours / maxEventDuration) * 100f); item.MTTRDurationPercent = Scaling.ScaleNumberToRange(interiumResult, 0, 100, 20, 100); } //Pull together the results into a single model MeanTimeToRestoreModel model = new MeanTimeToRestoreModel { TargetDevOpsPlatform = targetDevOpsPlatform, ResourceGroup = resourceGroup, MeanTimeToRestoreEvents = uiEvents, MTTRAverageDurationInHours = averageMTTR, MTTRAverageDurationDescription = mttr.GetMeanTimeToRestoreRating(averageMTTR), NumberOfDays = numberOfDays, MaxNumberOfItems = uiEvents.Count, TotalItems = events.Count }; return(model); } else { //Get sample data MeanTimeToRestore mttr = new MeanTimeToRestore(); float averageMTTR = CalculateMTTRDuration(GetSampleMTTREvents(resourceGroup)); List <MeanTimeToRestoreEvent> sampleEvents = GetSampleMTTREvents(resourceGroup); MeanTimeToRestoreModel model = new MeanTimeToRestoreModel { TargetDevOpsPlatform = targetDevOpsPlatform, ResourceGroup = resourceGroup, MeanTimeToRestoreEvents = sampleEvents, MTTRAverageDurationInHours = averageMTTR, MTTRAverageDurationDescription = mttr.GetMeanTimeToRestoreRating(averageMTTR), NumberOfDays = numberOfDays, MaxNumberOfItems = sampleEvents.Count, TotalItems = sampleEvents.Count }; return(model); } }
public async Task <LeadTimeForChangesModel> GetGitHubLeadTimesForChanges(bool getSampleData, string clientId, string clientSecret, TableStorageAuth tableStorageAuth, string owner, string repo, string mainBranch, string workflowName, string workflowId, int numberOfDays, int maxNumberOfItems, bool useCache) { ListUtility <PullRequestModel> utility = new ListUtility <PullRequestModel>(); LeadTimeForChanges leadTimeForChanges = new LeadTimeForChanges(); List <PullRequestModel> pullRequests = new List <PullRequestModel>(); if (getSampleData == false) { List <GitHubActionsRun> initialRuns = new List <GitHubActionsRun>(); BuildsDA buildsDA = new BuildsDA(); initialRuns = await buildsDA.GetGitHubActionRuns(clientId, clientSecret, tableStorageAuth, owner, repo, workflowName, workflowId, useCache); //Process all builds, filtering by main and feature branchs List <GitHubActionsRun> mainBranchRuns = new List <GitHubActionsRun>(); List <GitHubActionsRun> featureBranchRuns = new List <GitHubActionsRun>(); List <string> branches = new List <string>(); foreach (GitHubActionsRun item in initialRuns) { if (item.status == "completed" && item.created_at > DateTime.Now.AddDays(-numberOfDays)) { if (item.head_branch == mainBranch) { //Save the main branch mainBranchRuns.Add(item); } else { //Save the feature branches featureBranchRuns.Add(item); //Record all unique branches if (branches.Contains(item.head_branch) == false) { branches.Add(item.head_branch); } } } } //Process the lead time for changes List <KeyValuePair <DateTime, TimeSpan> > leadTimeForChangesList = new List <KeyValuePair <DateTime, TimeSpan> >(); foreach (string branch in branches) { List <GitHubActionsRun> branchBuilds = featureBranchRuns.Where(a => a.head_branch == branch).ToList(); PullRequestDA pullRequestDA = new PullRequestDA(); GitHubPR pr = await pullRequestDA.GetGitHubPullRequest(clientId, clientSecret, tableStorageAuth, owner, repo, branch, useCache); if (pr != null) { List <GitHubPRCommit> pullRequestCommits = await pullRequestDA.GetGitHubPullRequestCommits(clientId, clientSecret, tableStorageAuth, owner, repo, pr.number, useCache); List <Commit> commits = new List <Commit>(); foreach (GitHubPRCommit item in pullRequestCommits) { commits.Add(new Commit { commitId = item.sha, name = item.commit.committer.name, date = item.commit.committer.date }); } DateTime minTime = DateTime.MaxValue; DateTime maxTime = DateTime.MinValue; foreach (GitHubPRCommit pullRequestCommit in pullRequestCommits) { if (minTime > pullRequestCommit.commit.committer.date) { minTime = pullRequestCommit.commit.committer.date; } if (maxTime < pullRequestCommit.commit.committer.date) { maxTime = pullRequestCommit.commit.committer.date; } } foreach (GitHubActionsRun branchBuild in branchBuilds) { if (minTime > branchBuild.updated_at) { minTime = branchBuild.updated_at; } if (maxTime < branchBuild.updated_at) { maxTime = branchBuild.updated_at; } } PullRequestModel pullRequest = new PullRequestModel { PullRequestId = pr.number, Branch = branch, BuildCount = branchBuilds.Count, Commits = commits, StartDateTime = minTime, EndDateTime = maxTime, Status = pr.state, Url = $"https://github.com/{owner}/{repo}/pull/{pr.number}" }; //Convert the pull request status to the standard UI status if (pullRequest.Status == "closed") { pullRequest.Status = "completed"; } else if (pullRequest.Status == "open") { pullRequest.Status = "inProgress"; } else { pullRequest.Status = pullRequest.Status; } leadTimeForChangesList.Add(new KeyValuePair <DateTime, TimeSpan>(minTime, pullRequest.Duration)); pullRequests.Add(pullRequest); } } //Calculate the lead time for changes value, in hours float leadTime = leadTimeForChanges.ProcessLeadTimeForChanges(leadTimeForChangesList, numberOfDays); List <PullRequestModel> uiPullRequests = utility.GetLastNItems(pullRequests, maxNumberOfItems); float maxPullRequestDuration = 0f; foreach (PullRequestModel item in uiPullRequests) { if (item.Duration.TotalMinutes > maxPullRequestDuration) { maxPullRequestDuration = (float)item.Duration.TotalMinutes; } } foreach (PullRequestModel item in uiPullRequests) { float interiumResult = (((float)item.Duration.TotalMinutes / maxPullRequestDuration) * 100f); item.DurationPercent = Scaling.ScaleNumberToRange(interiumResult, 0, 100, 20, 100); } double totalHours = 0; foreach (GitHubActionsRun item in mainBranchRuns) { totalHours += (item.updated_at - item.created_at).TotalHours; } float averageBuildHours = 0; if (mainBranchRuns.Count > 0) { averageBuildHours = (float)totalHours / (float)mainBranchRuns.Count; } LeadTimeForChangesModel model = new LeadTimeForChangesModel { ProjectName = repo, TargetDevOpsPlatform = DevOpsPlatform.GitHub, AverageBuildHours = averageBuildHours, AveragePullRequestHours = leadTime, LeadTimeForChangesMetric = leadTime + averageBuildHours, LeadTimeForChangesMetricDescription = leadTimeForChanges.GetLeadTimeForChangesRating(leadTime), PullRequests = utility.GetLastNItems(pullRequests, maxNumberOfItems), NumberOfDays = numberOfDays, MaxNumberOfItems = uiPullRequests.Count, TotalItems = pullRequests.Count }; return(model); } else { List <PullRequestModel> samplePullRequests = utility.GetLastNItems(CreatePullRequestsSample(DevOpsPlatform.GitHub), maxNumberOfItems); LeadTimeForChangesModel model = new LeadTimeForChangesModel { ProjectName = repo, TargetDevOpsPlatform = DevOpsPlatform.GitHub, AverageBuildHours = 1f, AveragePullRequestHours = 20.33f, LeadTimeForChangesMetric = 20.33f + 1f, LeadTimeForChangesMetricDescription = "Elite", PullRequests = samplePullRequests, NumberOfDays = numberOfDays, MaxNumberOfItems = samplePullRequests.Count, TotalItems = samplePullRequests.Count }; return(model); } }
public async Task <LeadTimeForChangesModel> GetAzureDevOpsLeadTimesForChanges(bool getSampleData, string patToken, TableStorageAuth tableStorageAuth, string organization, string project, string repositoryId, string mainBranch, string buildName, int numberOfDays, int maxNumberOfItems, bool useCache) { ListUtility <PullRequestModel> utility = new ListUtility <PullRequestModel>(); LeadTimeForChanges leadTimeForChanges = new LeadTimeForChanges(); List <PullRequestModel> pullRequests = new List <PullRequestModel>(); if (getSampleData == false) { List <AzureDevOpsBuild> initialBuilds = new List <AzureDevOpsBuild>(); BuildsDA buildsDA = new BuildsDA(); initialBuilds = await buildsDA.GetAzureDevOpsBuilds(patToken, tableStorageAuth, organization, project, buildName, useCache); //Process all builds, filtering by main and feature branchs List <AzureDevOpsBuild> mainBranchBuilds = new List <AzureDevOpsBuild>(); List <AzureDevOpsBuild> featureBranchBuilds = new List <AzureDevOpsBuild>(); List <string> branches = new List <string>(); foreach (AzureDevOpsBuild item in initialBuilds) { if (item.status == "completed" && item.queueTime > DateTime.Now.AddDays(-numberOfDays)) { if (item.sourceBranch == mainBranch) { //Save the main branch mainBranchBuilds.Add(item); } else { //Save the feature branches featureBranchBuilds.Add(item); //Record all unique branches if (branches.Contains(item.branch) == false) { branches.Add(item.branch); } } } } //Process the lead time for changes List <KeyValuePair <DateTime, TimeSpan> > leadTimeForChangesList = new List <KeyValuePair <DateTime, TimeSpan> >(); foreach (string branch in branches) { List <AzureDevOpsBuild> branchBuilds = featureBranchBuilds.Where(a => a.sourceBranch == branch).ToList(); PullRequestDA pullRequestDA = new PullRequestDA(); AzureDevOpsPR pr = await pullRequestDA.GetAzureDevOpsPullRequest(patToken, tableStorageAuth, organization, project, repositoryId, branch, useCache); if (pr != null) { List <AzureDevOpsPRCommit> pullRequestCommits = await pullRequestDA.GetAzureDevOpsPullRequestCommits(patToken, tableStorageAuth, organization, project, repositoryId, pr.PullRequestId, useCache); List <Commit> commits = new List <Commit>(); foreach (AzureDevOpsPRCommit item in pullRequestCommits) { commits.Add(new Commit { commitId = item.commitId, name = item.committer.name, date = item.committer.date }); } DateTime minTime = DateTime.MaxValue; DateTime maxTime = DateTime.MinValue; foreach (AzureDevOpsPRCommit pullRequestCommit in pullRequestCommits) { if (minTime > pullRequestCommit.committer.date) { minTime = pullRequestCommit.committer.date; } if (maxTime < pullRequestCommit.committer.date) { maxTime = pullRequestCommit.committer.date; } } foreach (AzureDevOpsBuild branchBuild in branchBuilds) { if (minTime > branchBuild.finishTime) { minTime = branchBuild.finishTime; } if (maxTime < branchBuild.finishTime) { maxTime = branchBuild.finishTime; } } PullRequestModel pullRequest = new PullRequestModel { PullRequestId = pr.PullRequestId, Branch = branch, BuildCount = branchBuilds.Count, Commits = commits, StartDateTime = minTime, EndDateTime = maxTime, Status = pr.status, Url = $"https://dev.azure.com/{organization}/{project}/_git/{repositoryId}/pullrequest/{pr.PullRequestId}" }; leadTimeForChangesList.Add(new KeyValuePair <DateTime, TimeSpan>(minTime, pullRequest.Duration)); pullRequests.Add(pullRequest); } } //Calculate the lead time for changes value, in hours float leadTime = leadTimeForChanges.ProcessLeadTimeForChanges(leadTimeForChangesList, numberOfDays); List <PullRequestModel> uiPullRequests = utility.GetLastNItems(pullRequests, maxNumberOfItems); float maxPullRequestDuration = 0f; foreach (PullRequestModel item in uiPullRequests) { if (item.Duration.TotalMinutes > maxPullRequestDuration) { maxPullRequestDuration = (float)item.Duration.TotalMinutes; } } foreach (PullRequestModel item in uiPullRequests) { float interiumResult = (((float)item.Duration.TotalMinutes / maxPullRequestDuration) * 100f); item.DurationPercent = Scaling.ScaleNumberToRange(interiumResult, 0, 100, 20, 100); } double totalHours = 0; foreach (AzureDevOpsBuild item in mainBranchBuilds) { totalHours += (item.finishTime - item.queueTime).TotalHours; } float averageBuildHours = 0; if (mainBranchBuilds.Count > 0) { averageBuildHours = (float)totalHours / (float)mainBranchBuilds.Count; } LeadTimeForChangesModel model = new LeadTimeForChangesModel { ProjectName = project, TargetDevOpsPlatform = DevOpsPlatform.AzureDevOps, AverageBuildHours = averageBuildHours, AveragePullRequestHours = leadTime, LeadTimeForChangesMetric = leadTime + averageBuildHours, LeadTimeForChangesMetricDescription = leadTimeForChanges.GetLeadTimeForChangesRating(leadTime), PullRequests = uiPullRequests, NumberOfDays = numberOfDays, MaxNumberOfItems = uiPullRequests.Count, TotalItems = pullRequests.Count }; return(model); } else { //Get sample data List <PullRequestModel> samplePullRequests = utility.GetLastNItems(CreatePullRequestsSample(DevOpsPlatform.AzureDevOps), maxNumberOfItems); LeadTimeForChangesModel model = new LeadTimeForChangesModel { ProjectName = project, TargetDevOpsPlatform = DevOpsPlatform.AzureDevOps, AverageBuildHours = 1f, AveragePullRequestHours = 12f, LeadTimeForChangesMetric = 12f + 1f, LeadTimeForChangesMetricDescription = "Elite", PullRequests = samplePullRequests, NumberOfDays = numberOfDays, MaxNumberOfItems = samplePullRequests.Count, TotalItems = samplePullRequests.Count }; return(model); } }