-
Notifications
You must be signed in to change notification settings - Fork 1
Add Bot/Community PR categorization to digest pages #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,9 +63,13 @@ public static string GenerateIndex(string archivesDir, string outputsDir) | |
| statsHtml = $""" | ||
| <div class="stats-grid"> | ||
| <div class="stat-card"> | ||
| <div class="stat-value">{analyzerResult.PullRequestCount}</div> | ||
| <div class="stat-value">{analyzerResult.PullRequestTotalCount}</div> | ||
| <div class="stat-label">マージされたPR</div> | ||
| </div> | ||
| <div class="stat-card"> | ||
| <div class="stat-value">{analyzerResult.PullRequestCountForBot}</div> | ||
| <div class="stat-label">マージされたPR(Bot)</div> | ||
| </div> | ||
| <div class="stat-card"> | ||
| <div class="stat-value">{analyzerResult.LabelCount}</div> | ||
| <div class="stat-label">ラベル種類</div> | ||
|
|
@@ -120,6 +124,7 @@ public static string GenerateHtmlFromMarkdown(string startTargetDate, string mar | |
| } | ||
|
|
||
| var analyzerResult = PullReqeustAnalayzer.Analayze(document); | ||
| var categoryViewHtml = GenerateCategorizedTocHtml(analyzerResult); | ||
| var labelViewHtml = GenerateLabelViewHtml(analyzerResult); | ||
|
|
||
| var content = $""" | ||
|
|
@@ -128,11 +133,15 @@ public static string GenerateHtmlFromMarkdown(string startTargetDate, string mar | |
| <hr> | ||
| <div class="view-tabs"> | ||
| <button class="view-tab active" data-view="list">一覧</button> | ||
| <button class="view-tab" data-view="category">カテゴリ別</button> | ||
| <button class="view-tab" data-view="label">ラベル別</button> | ||
| </div> | ||
| <div id="list-view" class="view-panel"> | ||
| {tocHtml} | ||
| </div> | ||
| <div id="category-view" class="view-panel" style="display:none"> | ||
| {categoryViewHtml} | ||
| </div> | ||
| <div id="label-view" class="view-panel" style="display:none"> | ||
| {labelViewHtml} | ||
| </div> | ||
|
|
@@ -164,49 +173,83 @@ private static string GenerateLabelViewHtml(PullReqeustAnalayzer.AnalayzerResult | |
|
|
||
| foreach (var heading in headingBlocks) | ||
| { | ||
| // Extract PR number and title from HeadingBlock inlines | ||
| var pullRequestNumber = ""; | ||
| var titleText = ""; | ||
| AppendHeadingListItem(ref builder, heading); | ||
| } | ||
|
|
||
| builder.AppendLiteral($" </ol>{Environment.NewLine}"); | ||
| builder.AppendLiteral($"</details>{Environment.NewLine}"); | ||
| } | ||
|
|
||
| return builder.ToStringAndClear(); | ||
| } | ||
|
|
||
| private static string GenerateCategorizedTocHtml(PullReqeustAnalayzer.AnalayzerResult analyzerResult) | ||
| { | ||
| var builder = new DefaultInterpolatedStringHandler(0, 0); | ||
| builder.AppendLiteral($"<h3>カテゴリ別PR一覧</h3>{Environment.NewLine}"); | ||
|
|
||
| // Community PRs (expanded) | ||
| var communityCount = analyzerResult.CommunityPullRequestHeadingSpan.Length; | ||
| builder.AppendLiteral($"<details class=\"label-group\">{Environment.NewLine}"); | ||
| builder.AppendLiteral($" <summary class=\"label-group-summary\">Community PRs <span class=\"label-pr-count\">({communityCount} PRs)</span></summary>{Environment.NewLine}"); | ||
| builder.AppendLiteral($" <ol class=\"label-pr-list\">{Environment.NewLine}"); | ||
| foreach (var heading in analyzerResult.CommunityPullRequestHeadingSpan) | ||
| { | ||
| AppendHeadingListItem(ref builder, heading); | ||
| } | ||
| builder.AppendLiteral($" </ol>{Environment.NewLine}"); | ||
| builder.AppendLiteral($"</details>{Environment.NewLine}"); | ||
|
|
||
| // Bot PRs (collapsed) | ||
| var botCount = analyzerResult.BotPullRequestHeadings?.Count ?? 0; | ||
| builder.AppendLiteral($"<details class=\"label-group\">{Environment.NewLine}"); | ||
| builder.AppendLiteral($" <summary class=\"label-group-summary\">Bot PRs <span class=\"label-pr-count\">({botCount} PRs)</span></summary>{Environment.NewLine}"); | ||
| builder.AppendLiteral($" <ol class=\"label-pr-list\">{Environment.NewLine}"); | ||
| foreach (var heading in analyzerResult.BotPullRequestHeadings ?? []) | ||
| { | ||
| AppendHeadingListItem(ref builder, heading); | ||
| } | ||
| builder.AppendLiteral($" </ol>{Environment.NewLine}"); | ||
| builder.AppendLiteral($"</details>{Environment.NewLine}"); | ||
|
|
||
| return builder.ToStringAndClear(); | ||
| } | ||
|
|
||
| private static void AppendHeadingListItem(ref DefaultInterpolatedStringHandler builder, HeadingBlock heading) | ||
| { | ||
| var pullRequestNumber = ""; | ||
| var titleText = ""; | ||
|
|
||
| var inline = heading.Inline?.FirstChild; | ||
| while (inline is not null) | ||
| var inline = heading.Inline?.FirstChild; | ||
| while (inline is not null) | ||
| { | ||
| if (inline is LinkInline linkInline) | ||
| { | ||
| var linkChild = linkInline.FirstChild; | ||
| while (linkChild is not null) | ||
| { | ||
| if (inline is LinkInline linkInline) | ||
| if (linkChild is LiteralInline lit) | ||
| { | ||
| // The link text contains the PR number like "#124237" | ||
| var linkChild = linkInline.FirstChild; | ||
| while (linkChild is not null) | ||
| { | ||
| if (linkChild is LiteralInline lit) | ||
| { | ||
| pullRequestNumber = lit.Content.ToString(); | ||
| } | ||
| linkChild = linkChild.NextSibling; | ||
| } | ||
| pullRequestNumber = lit.Content.ToString(); | ||
| } | ||
| else if (inline is LiteralInline literal) | ||
| { | ||
| titleText += literal.Content.ToString(); | ||
| } | ||
| else if (inline is CodeInline codeInline) | ||
| { | ||
| titleText += codeInline.Content; | ||
| } | ||
| inline = inline.NextSibling; | ||
| linkChild = linkChild.NextSibling; | ||
| } | ||
|
|
||
| // Derive anchor ID from PR number (remove '#') | ||
| var anchorId = pullRequestNumber.TrimStart('#'); | ||
| var displayText = $"{pullRequestNumber} {titleText.Trim()}"; | ||
|
|
||
| builder.AppendLiteral($" <li><a href=\"#{anchorId}\">{System.Net.WebUtility.HtmlEncode(displayText)}</a></li>{Environment.NewLine}"); | ||
| } | ||
|
|
||
| builder.AppendLiteral($" </ol>{Environment.NewLine}"); | ||
| builder.AppendLiteral($"</details>{Environment.NewLine}"); | ||
| else if (inline is LiteralInline literal) | ||
| { | ||
| titleText += literal.Content.ToString(); | ||
| } | ||
| else if (inline is CodeInline codeInline) | ||
| { | ||
| titleText += codeInline.Content; | ||
| } | ||
| inline = inline.NextSibling; | ||
| } | ||
|
|
||
| return builder.ToStringAndClear(); | ||
| var anchorId = pullRequestNumber.TrimStart('#'); | ||
| var displayText = $"{pullRequestNumber} {titleText.Trim()}"; | ||
|
|
||
| builder.AppendLiteral($" <li><a href=\"#{anchorId}\">{System.Net.WebUtility.HtmlEncode(displayText)}</a></li>{Environment.NewLine}"); | ||
| } | ||
|
|
||
| private static string GenerateTemplateHtml(string title, string subTitle, string content, bool includeViewScript = false) | ||
|
|
@@ -576,7 +619,7 @@ footer p { | |
|
|
||
| .stats-grid { | ||
| display: grid; | ||
| grid-template-columns: repeat(2, 1fr); | ||
| grid-template-columns: repeat(3, 1fr); | ||
|
||
| gap: 16px; | ||
| margin: 16px 0 24px 0; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -9,11 +9,14 @@ internal static class PullReqeustAnalayzer | |||||
| public static AnalayzerResult Analayze(MarkdownDocument document) | ||||||
| { | ||||||
| var tableOfContents = false; | ||||||
| var pullRequestCount = 0; | ||||||
| var pullRequestTotalCount = 0; | ||||||
| var pullRequestCountForBot = 0; | ||||||
| HeadingBlock? nextPrNumber = null; | ||||||
| HashSet<string>? prNumberTable = null; | ||||||
| Dictionary<string, List<HeadingBlock>>? labelTable = new(); | ||||||
| Dictionary<string, string>? labelColorMap = new(); | ||||||
| List<HeadingBlock> botPullRequestHeadings = new(); | ||||||
| List<HeadingBlock> communityPrHeadings = new(); | ||||||
|
prozolic marked this conversation as resolved.
|
||||||
|
|
||||||
| foreach (var block in document) | ||||||
| { | ||||||
|
|
@@ -29,10 +32,39 @@ public static AnalayzerResult Analayze(MarkdownDocument document) | |||||
| { | ||||||
| if (tableOfContents && nextPrNumber is not null) | ||||||
| { | ||||||
| var labelBlock = listBlock.Descendants<ListItemBlock>().Skip(3).FirstOrDefault(); | ||||||
| var labels = labelBlock?.Descendants<LiteralInline>() | ||||||
| .Where(l => | ||||||
| { | ||||||
| // pullRequestInfo is 4 items. | ||||||
| // 0: User | ||||||
| // 1: Created at | ||||||
| // 2: Merged at | ||||||
| // 3: Labels | ||||||
| var pullRequestInfo = listBlock.Descendants<ListItemBlock>().ToArray(); | ||||||
|
|
||||||
| var userBlock = pullRequestInfo[0]; | ||||||
| var user = userBlock?.Descendants<LiteralInline>().Skip(1).FirstOrDefault(); | ||||||
|
|
||||||
| if (user is not null) | ||||||
| { | ||||||
| var userName = user.Content.ToString().Trim(); | ||||||
|
|
||||||
| // check ..[bot].. or @Copilot to count bot PRs | ||||||
| if (userName.EndsWith("[bot]", StringComparison.OrdinalIgnoreCase) || | ||||||
|
prozolic marked this conversation as resolved.
|
||||||
| userName.IndexOf("@Copilot", StringComparison.OrdinalIgnoreCase) > -1) | ||||||
|
prozolic marked this conversation as resolved.
|
||||||
| { | ||||||
| pullRequestCountForBot++; | ||||||
| botPullRequestHeadings.Add(nextPrNumber); | ||||||
| } | ||||||
| else | ||||||
| { | ||||||
| communityPrHeadings.Add(nextPrNumber); | ||||||
| } | ||||||
| } | ||||||
| else | ||||||
| { | ||||||
| communityPrHeadings.Add(nextPrNumber); | ||||||
| } | ||||||
|
|
||||||
| var labelBlock = pullRequestInfo[3]; | ||||||
|
prozolic marked this conversation as resolved.
|
||||||
| var labels = labelBlock?.Descendants<LiteralInline>().Where(l => { | ||||||
| var labelText = l.Content.ToString(); | ||||||
| return !string.IsNullOrWhiteSpace(labelText) && !labelText.Contains("ラベル"); | ||||||
| }); | ||||||
|
|
@@ -89,7 +121,7 @@ public static AnalayzerResult Analayze(MarkdownDocument document) | |||||
| { | ||||||
| foreach (var listItemBlock in listBlock.Descendants<ListItemBlock>()) | ||||||
| { | ||||||
| pullRequestCount++; | ||||||
| pullRequestTotalCount++; | ||||||
| var prNumber = listItemBlock.Descendants<LinkInline>().FirstOrDefault(); | ||||||
| if (prNumber is not null) | ||||||
| { | ||||||
|
|
@@ -105,17 +137,25 @@ public static AnalayzerResult Analayze(MarkdownDocument document) | |||||
|
|
||||||
| return new AnalayzerResult | ||||||
| { | ||||||
| PullRequestCount = pullRequestCount, | ||||||
| PullRequestTotalCount = pullRequestTotalCount, | ||||||
| PullRequestCountForBot = pullRequestCountForBot, | ||||||
| LabelInfo = labelTable, | ||||||
| LabelColorMap = labelColorMap | ||||||
| LabelColorMap = labelColorMap, | ||||||
| BotPullRequestHeadings = botPullRequestHeadings, | ||||||
| CommunityPullRequestHeadings = communityPrHeadings | ||||||
| }; | ||||||
| } | ||||||
|
|
||||||
| public ref struct AnalayzerResult | ||||||
| { | ||||||
| public int PullRequestCount; | ||||||
| public int PullRequestTotalCount; | ||||||
| public int PullRequestCountForBot; | ||||||
| public Dictionary<string, List<HeadingBlock>>? LabelInfo; | ||||||
| public Dictionary<string, string>? LabelColorMap; | ||||||
| public List<HeadingBlock>? BotPullRequestHeadings; | ||||||
|
||||||
| public List<HeadingBlock>? BotPullRequestHeadings; | |
| public List<HeadingBlock> BotPullRequestHeadings; |
Uh oh!
There was an error while loading. Please reload this page.