private float GetFormulaPercentile(QuantifierResult quantifierResult) { var contextFormulaPercentile = Context.FormulaPercentile.First(f => f.Item1 == quantifierResult.Formula).Item2; return(GetPercentile(quantifierResult.FormulaLinesChanged, contextFormulaPercentile)); }
private async Task UpdateCommentOnPullRequest( PullRequestEventPayload payload, IGitHubClientAdapter gitHubClientAdapter, QuantifierResult quantifierClientResult, string quantifierContextLink) { // delete existing comments created by us var existingComments = await gitHubClientAdapter.GetIssueCommentsAsync(payload.Repository.Id, payload.PullRequest.Number); var existingCommentsCreatedByUs = existingComments.Where( ec => ec.User.Login.Equals($"{gitHubClientAdapter.GitHubAppSettings.Name}[bot]")); foreach (var existingComment in existingCommentsCreatedByUs) { await gitHubClientAdapter.DeleteIssueCommentAsync(payload.Repository.Id, existingComment.Id); } // create a new comment on the issue var comment = await quantifierClientResult.ToMarkdownCommentAsync( payload.Repository.HtmlUrl, quantifierContextLink, payload.PullRequest.HtmlUrl, payload.PullRequest.User.Login, ShouldPostAnonymousFeedbackLink(payload), new MarkdownCommentOptions { CollapsePullRequestQuantifiedSection = true }); await gitHubClientAdapter.CreateIssueCommentAsync( payload.Repository.Id, payload.PullRequest.Number, comment); }
private void CountTotalChanges( QuantifierInput quantifierInput, QuantifierResult quantifierResult) { quantifierResult.QuantifiedLinesAdded = quantifierInput.Changes.Sum(c => c.QuantifiedLinesAdded); quantifierResult.QuantifiedLinesDeleted = quantifierInput.Changes.Sum(c => c.QuantifiedLinesDeleted); }
private float GetAdditionDeletionPercentile( QuantifierResult quantifierResult, bool addition) { return(addition ? GetPercentile(quantifierResult.QuantifiedLinesAdded, Context.AdditionPercentile) : GetPercentile(quantifierResult.QuantifiedLinesDeleted, Context.DeletionPercentile)); }
private async Task ApplyLabelToPullRequest( PullRequestEventPayload payload, IGitHubClientAdapter gitHubClientAdapter, QuantifierResult quantifierClientResult, IEnumerable <string> labelOptionsFromContext) { // create a new label in the repository if doesn't exist try { var existingLabel = await gitHubClientAdapter.GetLabelAsync( payload.Repository.Id, quantifierClientResult.Label); } catch (NotFoundException) { // create new label var color = Color.FromName(quantifierClientResult.Color); await gitHubClientAdapter.CreateLabelAsync( payload.Repository.Id, new NewLabel(quantifierClientResult.Label, ConvertToHex(color))); } // remove any previous labels applied if it's different // labels do not have the property of who applied them // so we use string matching against the label options present in the context // if label strings have changed in context since last PR update, this will break var existingLabels = await gitHubClientAdapter.GetIssueLabelsAsync(payload.Repository.Id, payload.PullRequest.Number); var existingLabelsByUs = existingLabels.Select(el => el.Name).Intersect(labelOptionsFromContext).ToList(); foreach (var existingLabel in existingLabelsByUs) { if (existingLabel == quantifierClientResult.Label) { continue; } await gitHubClientAdapter.RemoveLabelFromIssueAsync( payload.Repository.Id, payload.PullRequest.Number, existingLabel); } // apply new label to pull request await gitHubClientAdapter.ApplyLabelAsync( payload.Repository.Id, payload.PullRequest.Number, new[] { quantifierClientResult.Label }); }
private int GetChangeNumber( QuantifierResult quantifierResult, ThresholdFormula formula) { return(formula switch { ThresholdFormula.Sum => quantifierResult.QuantifiedLinesAdded + quantifierResult.QuantifiedLinesDeleted, ThresholdFormula.Avg => (quantifierResult.QuantifiedLinesAdded + quantifierResult.QuantifiedLinesDeleted) / 2, ThresholdFormula.Min => Math.Min( quantifierResult.QuantifiedLinesAdded, quantifierResult.QuantifiedLinesDeleted), ThresholdFormula.Max => Math.Max( quantifierResult.QuantifiedLinesAdded, quantifierResult.QuantifiedLinesDeleted), _ => 0 });
private void SetLabel(QuantifierResult quantifierResult) { if (quantifierResult.QuantifiedLinesDeleted == 0 && quantifierResult.QuantifiedLinesAdded == 0) { quantifierResult.Label = "No Changes"; quantifierResult.Color = nameof(Color.Green); return; } // for now to set the label use the absolute values addition/deletion and compare them with the thresholds // the percentile will be displayed saying that if your change has this number // of lines additions then you are at this percentile within this context foreach (var contextThreshold in Context.Thresholds.OrderBy(t => t.Value)) { // we set the label from the thresholds and exit when we have first value threshold grater then percentile quantifierResult.Label = contextThreshold.Label; quantifierResult.Color = contextThreshold.Color; quantifierResult.Formula = contextThreshold.Formula; quantifierResult.FormulaLinesChanged = GetChangeNumber(quantifierResult, contextThreshold.Formula); if (quantifierResult.FormulaLinesChanged <= contextThreshold.Value) { break; } } // in case no addition/deletion found then we won't be able to set the change percentile. if (Context.AdditionPercentile == null || Context.DeletionPercentile == null || Context.FormulaPercentile == null || Context.AdditionPercentile.Count == 0 || Context.DeletionPercentile.Count == 0 || !Context.FormulaPercentile.Any()) { return; } quantifierResult.PercentileAddition = MathF.Round(GetAdditionDeletionPercentile(quantifierResult, true), 2); quantifierResult.PercentileDeletion = MathF.Round( GetAdditionDeletionPercentile(quantifierResult, false), 2); quantifierResult.FormulaPercentile = MathF.Round(GetFormulaPercentile(quantifierResult), 2); }
private float GetFormulaPercentile(QuantifierResult quantifierResult) { var finalValue = quantifierResult.Formula switch { ThresholdFormula.Sum => quantifierResult.QuantifiedLinesAdded + quantifierResult.QuantifiedLinesDeleted, ThresholdFormula.Avg => (quantifierResult.QuantifiedLinesAdded + quantifierResult.QuantifiedLinesDeleted) / 2, ThresholdFormula.Min => Math.Min( quantifierResult.QuantifiedLinesAdded, quantifierResult.QuantifiedLinesDeleted), ThresholdFormula.Max => Math.Max( quantifierResult.QuantifiedLinesAdded, quantifierResult.QuantifiedLinesDeleted), _ => throw new ArgumentOutOfRangeException(), }; var contextFormulaPercentile = Context.FormulaPercentile.First(f => f.Item1 == quantifierResult.Formula).Item2; return(GetPercentile(finalValue, contextFormulaPercentile)); }
private async Task <QuantifierResult> Compute(QuantifierInput quantifierInput) { QuantifierResult quantifierResult = null; await Task.Factory.StartNew( () => { quantifierResult = new QuantifierResult { QuantifierInput = quantifierInput }; // involve context and compute Parallel.ForEach(quantifierInput.Changes, ApplyContext); CountTotalChanges(quantifierInput, quantifierResult); // compute the label using the context percentile information and the thresholds SetLabel(quantifierResult); }); return(quantifierResult); }
public static async Task <string> ToConsoleOutput(this QuantifierResult quantifierResult) { var stubble = new StubbleBuilder() .Configure(settings => { settings.SetIgnoreCaseOnKeyLookup(true); }).Build(); using var stream = new StreamReader( Assembly.GetExecutingAssembly().GetManifestResourceStream( $"{typeof(QuantifierResultExtensions).Namespace}.ConsoleOutput.mustache") !); var consoleOutput = await stubble.RenderAsync( await stream.ReadToEndAsync(), new { quantifierResult.Label, quantifierResult.QuantifiedLinesAdded, quantifierResult.QuantifiedLinesDeleted, quantifierResult.PercentileAddition, quantifierResult.PercentileDeletion, quantifierResult.FormulaPercentile, Formula = quantifierResult.Formula.ToString(), }); return(consoleOutput); }
public static async Task <string> ToMarkdownCommentAsync( this QuantifierResult quantifierResult, string repositoryLink, string contextFileLink, string pullRequestLink, string authorName, MarkdownCommentOptions markdownCommentOptions = null) { markdownCommentOptions ??= new MarkdownCommentOptions(); var stubble = new StubbleBuilder() .Configure(settings => { settings.SetIgnoreCaseOnKeyLookup(true); }).Build(); using var stream = new StreamReader( Assembly.GetExecutingAssembly().GetManifestResourceStream( $"{typeof(QuantifierResultExtensions).Namespace}.QuantifierComment.mustache") !); var feedbackLinkThumbsUp = CreateFeedbackLink( "ThumbsUp", authorName, repositoryLink, pullRequestLink); var feedbackLinkNeutral = CreateFeedbackLink( "Neutral", authorName, repositoryLink, pullRequestLink); var feedbackLinkThumbsDown = CreateFeedbackLink( "ThumbsDown", authorName, repositoryLink, pullRequestLink); var comment = await stubble.RenderAsync( await stream.ReadToEndAsync(), new { quantifierResult.Color, quantifierResult.Label, quantifierResult.QuantifiedLinesAdded, quantifierResult.QuantifiedLinesDeleted, quantifierResult.PercentileAddition, quantifierResult.PercentileDeletion, quantifierResult.FormulaPercentile, Formula = quantifierResult.Formula.ToString(), ContextFileLink = contextFileLink, FeedbackLinkUp = feedbackLinkThumbsUp, FeedbackLinkNeutral = feedbackLinkNeutral, FeedbackLinkDown = feedbackLinkThumbsDown, TotalFilesChanged = quantifierResult.QuantifierInput.Changes.Count, Details = string.Join( Environment.NewLine, quantifierResult.QuantifierInput.Changes .GroupBy(c => c.FileExtension) .Select( g => $"{g.Key} : +{g.Sum(v => v.QuantifiedLinesAdded)} -{g.Sum(v => v.QuantifiedLinesDeleted)}")), CollapseChangesSummarySection = markdownCommentOptions.CollapseChangesSummarySection ? string.Empty : "open", CollapsePullRequestQuantifiedSection = markdownCommentOptions.CollapsePullRequestQuantifiedSection ? string.Empty : "open" }); return(comment); }
public static async Task <string> ToMarkdownCommentAsync( this QuantifierResult quantifierResult, string repositoryLink, string contextFileLink, string pullRequestLink, string authorName, bool anonymous = true, MarkdownCommentOptions markdownCommentOptions = null) { markdownCommentOptions ??= new MarkdownCommentOptions(); var stubble = new StubbleBuilder() .Configure(settings => { settings.SetIgnoreCaseOnKeyLookup(true); }).Build(); using var stream = new StreamReader( Assembly.GetExecutingAssembly().GetManifestResourceStream( $"{typeof(QuantifierResultExtensions).Namespace}.Mustache.QuantifierComment.mustache") !); var feedbackLinkThumbsUp = CreateFeedbackLink( "ThumbsUp", authorName, repositoryLink, pullRequestLink, anonymous); var feedbackLinkNeutral = CreateFeedbackLink( "Neutral", authorName, repositoryLink, pullRequestLink, anonymous); var feedbackLinkThumbsDown = CreateFeedbackLink( "ThumbsDown", authorName, repositoryLink, pullRequestLink, anonymous); var contextFormulaPercentile = quantifierResult.Context.FormulaPercentile.First(f => f.Item1 == quantifierResult.Formula).Item2; var(idealSizeLowerBound, idealSizeUpperBound) = GetIdealChangeCountRange(contextFormulaPercentile); var detailsByFileExt = quantifierResult.QuantifierInput.Changes .Where(c => !string.IsNullOrEmpty(c.FileExtension)) .GroupBy(c => c.FileExtension) .Select( g => $"{g.Key} : +{g.Sum(v => v.QuantifiedLinesAdded)} -{g.Sum(v => v.QuantifiedLinesDeleted)}") .Concat( quantifierResult.QuantifierInput.Changes .Where(c => string.IsNullOrEmpty(c.FileExtension)) .Select(c => $"{c.FilePath} : +{c.QuantifiedLinesAdded} -{c.QuantifiedLinesDeleted}")); var comment = await stubble.RenderAsync( await stream.ReadToEndAsync(), new { // lower th color str because of a bug in shield service // https://img.shields.io/static/v1?label=Quantified&message=Extra%20Small&color=Red is not respecting the red color, // but https://img.shields.io/static/v1?label=Quantified&message=Extra%20Small&color=red is working Color = quantifierResult.Color.ToLower(), quantifierResult.Label, // encode the label in case contains space EncodedLabel = Uri.EscapeUriString(quantifierResult.Label), quantifierResult.QuantifiedLinesAdded, quantifierResult.QuantifiedLinesDeleted, quantifierResult.FormulaLinesChanged, quantifierResult.PercentileAddition, quantifierResult.PercentileDeletion, quantifierResult.FormulaPercentile, IdealSizeLowerBound = idealSizeLowerBound, IdealSizeUpperBound = idealSizeUpperBound, IsIdealSize = quantifierResult.FormulaLinesChanged >= idealSizeLowerBound && quantifierResult.FormulaLinesChanged <= idealSizeUpperBound, Formula = quantifierResult.Formula.ToString(), ContextFileLink = !string.IsNullOrWhiteSpace(contextFileLink) ? contextFileLink : "https://github.com/microsoft/PullRequestQuantifier/blob/main/docs/prquantifier-yaml.md", FeedbackLinkUp = feedbackLinkThumbsUp, FeedbackLinkNeutral = feedbackLinkNeutral, FeedbackLinkDown = feedbackLinkThumbsDown, TotalFilesChanged = quantifierResult.QuantifierInput.Changes.Count, Details = string.Join(Environment.NewLine, detailsByFileExt), CollapsePullRequestQuantifiedSection = markdownCommentOptions.CollapsePullRequestQuantifiedSection ? string.Empty : "open" }); return(comment); }
private static void PrintResult( QuantifierResult quantifierResult, ClientOutputType clientOutputType) { Console.ForegroundColor = QuantifyClientHelper.GetColor(quantifierResult.Color); switch (clientOutputType) { case ClientOutputType.None: { Console.WriteLine(quantifierResult.ToConsoleOutput().Result); break; } case ClientOutputType.Detailed: { Console.WriteLine(JsonSerializer.Serialize( quantifierResult, new JsonSerializerOptions { WriteIndented = true })); break; } case ClientOutputType.SummaryByFile: { Console.WriteLine(JsonSerializer.Serialize( new { quantifierResult.Label, quantifierResult.QuantifiedLinesAdded, quantifierResult.QuantifiedLinesDeleted, Formula = quantifierResult.Formula.ToString(), quantifierResult.PercentileAddition, quantifierResult.PercentileDeletion, Details = quantifierResult.QuantifierInput.Changes.Select(s => new { s.FilePath, s.QuantifiedLinesAdded, s.QuantifiedLinesDeleted, s.DiscardFromCounting }) }, new JsonSerializerOptions { WriteIndented = true })); break; } case ClientOutputType.SummaryByExt: { Console.WriteLine(JsonSerializer.Serialize( new { quantifierResult.Label, quantifierResult.QuantifiedLinesAdded, quantifierResult.QuantifiedLinesDeleted, Formula = quantifierResult.Formula.ToString(), quantifierResult.PercentileAddition, quantifierResult.PercentileDeletion, Details = quantifierResult.QuantifierInput.Changes.GroupBy(c => c.FileExtension).Select(g => new { FileExtension = g.Key, QuantifiedLinesAdded = g.Sum(v => v.QuantifiedLinesAdded), QuantifiedLinesDeleted = g.Sum(v => v.QuantifiedLinesDeleted) }) }, new JsonSerializerOptions { WriteIndented = true })); break; } default: throw new ArgumentOutOfRangeException(nameof(clientOutputType), clientOutputType, null); } Console.ResetColor(); }