Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions PanoramicData.IssueAssessment/Execution/CategoryExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,7 @@ private static CategoryResult BuildCategoryResult(AssessmentCategory category, I
.Zip(category.Assessments)
.Where(pair => pair.First.Outcome != AssessmentOutcome.NotApplicable)];

double? rawScore = null;
if (applicable.Count > 0 && applicable.TrueForAll(pair => pair.Result.Score.RawScore.HasValue))
{
rawScore = applicable.Sum(pair => pair.Result.Score.RawScore!.Value * pair.Assessment.Weight);
}

double? rawScore = WeightedRawScore(applicable);
double? normalizedScore = rawScore.HasValue
? ScoreCalculator.Normalize(rawScore.Value, minPossibleRaw, maxPossibleRaw)
: null;
Expand All @@ -176,4 +171,12 @@ private static CategoryResult BuildCategoryResult(AssessmentCategory category, I
string reason = $"Executed {resultList.Count} assessment(s) in category '{category.Name}'.";
return new CategoryResult(category.Name, score, reason, resultList) { Weight = category.Weight };
}

/// <summary>
/// The weighted sum of the applicable assessments' raw scores; null when none applies or any of them has no score.
/// </summary>
private static double? WeightedRawScore(List<(AssessmentResult Result, Assessment Assessment)> applicable)
=> applicable.Count > 0 && applicable.TrueForAll(pair => pair.Result.Score.RawScore.HasValue)
? applicable.Sum(pair => pair.Result.Score.RawScore!.Value * pair.Assessment.Weight)
: null;
}
19 changes: 8 additions & 11 deletions PanoramicData.IssueAssessment/Mapping/GitHubIssueMapper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.RegularExpressions;
using static PanoramicData.IssueAssessment.Mapping.JsonReading;

namespace PanoramicData.IssueAssessment.Mapping;

Expand Down Expand Up @@ -29,38 +30,38 @@ public static NormalizedIssue FromRestJson(JsonElement issue)
}

ImmutableList<string> labels = Labels(issue);
string body = Text(issue, "body") ?? string.Empty;
string body = StringAt(issue, "body") ?? string.Empty;

return new NormalizedIssue
{
Key = Key(issue),
Title = Text(issue, "title"),
Title = StringAt(issue, "title"),
Description = body,
IssueType = IssueType(labels),
Priority = Priority(labels),
Status = Text(issue, "state"),
Status = StringAt(issue, "state"),
Assignee = FirstAssignee(issue),
Components = Components(labels),
Labels = labels,
AttachmentsCount = MarkdownImage().Count(body) + ImageUrl().Count(body),
SupportsFixVersion = false,
DueDate = issue.TryGetProperty("milestone", out JsonElement milestone) ? Text(milestone, "due_on") : null,
Url = Text(issue, "html_url"),
DueDate = issue.TryGetProperty("milestone", out JsonElement milestone) ? StringAt(milestone, "due_on") : null,
Url = StringAt(issue, "html_url"),
Platform = "GitHub",
};
}

private static ImmutableList<string> Labels(JsonElement issue)
=> issue.TryGetProperty("labels", out JsonElement labels) && labels.ValueKind == JsonValueKind.Array
? [.. labels.EnumerateArray().Select(label => label.ValueKind == JsonValueKind.String ? label.GetString() : Text(label, "name")).OfType<string>()]
? [.. labels.EnumerateArray().Select(label => label.ValueKind == JsonValueKind.String ? label.GetString() : StringAt(label, "name")).OfType<string>()]
: [];

private static string? Key(JsonElement issue)
=> issue.TryGetProperty("number", out JsonElement number) && number.ValueKind == JsonValueKind.Number ? $"#{number.GetRawText()}" : null;

private static string? FirstAssignee(JsonElement issue)
=> issue.TryGetProperty("assignees", out JsonElement assignees) && assignees.ValueKind == JsonValueKind.Array && assignees.GetArrayLength() > 0
? Text(assignees[0], "login")
? StringAt(assignees[0], "login")
: null;

private static string IssueType(ImmutableList<string> labels)
Expand Down Expand Up @@ -92,10 +93,6 @@ private static ImmutableList<string> Components(ImmutableList<string> labels)
.Select(match => match.Groups[2].Value.Trim())
.Distinct(StringComparer.Ordinal)];

private static string? Text(JsonElement element, string property)
=> element.ValueKind == JsonValueKind.Object && element.TryGetProperty(property, out JsonElement value) && value.ValueKind == JsonValueKind.String
? value.GetString()
: null;

[GeneratedRegex("enhancement|feature|improvement|story", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant, 1000)]
private static partial Regex FeatureLabel();
Expand Down
38 changes: 16 additions & 22 deletions PanoramicData.IssueAssessment/Mapping/JiraIssueMapper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Frozen;
using System.Text;
using static PanoramicData.IssueAssessment.Mapping.JsonReading;

namespace PanoramicData.IssueAssessment.Mapping;

Expand Down Expand Up @@ -45,22 +46,22 @@ public static NormalizedIssue FromRestJson(JsonElement issue)
public static NormalizedIssue FromRestJson(JsonElement issue, Uri? baseUrl)
{
JsonElement fields = Fields(issue);
string? key = Text(issue, "key");
string? key = StringAt(issue, "key");
return new NormalizedIssue
{
Key = key,
Project = Text(fields, "project", "key"),
Title = Text(fields, "summary"),
Project = StringAt(fields, "project", "key"),
Title = StringAt(fields, "summary"),
Description = Description(fields),
IssueType = Text(fields, "issuetype", "name"),
Priority = Text(fields, "priority", "name"),
Status = Text(fields, "status", "name"),
Assignee = Text(fields, "assignee", "displayName"),
IssueType = StringAt(fields, "issuetype", "name"),
Priority = StringAt(fields, "priority", "name"),
Status = StringAt(fields, "status", "name"),
Assignee = StringAt(fields, "assignee", "displayName"),
Components = Names(fields, "components"),
Labels = Strings(fields, "labels"),
FixVersions = Names(fields, "fixVersions"),
AttachmentsCount = ArrayLength(fields, "attachment"),
DueDate = Text(fields, "duedate"),
DueDate = StringAt(fields, "duedate"),
Url = BrowseUrl(baseUrl, key),
Platform = "Jira",
};
Expand Down Expand Up @@ -106,7 +107,7 @@ public static string FlattenAdf(JsonElement document)

private static void AppendBlock(JsonElement node, StringBuilder text, string listPrefix)
{
string type = Text(node, "type") ?? string.Empty;
string type = StringAt(node, "type") ?? string.Empty;
if (ListMarkers.TryGetValue(type, out string? listMarker))
{
AppendList(node, text, listPrefix + listMarker);
Expand Down Expand Up @@ -150,7 +151,7 @@ private static void AppendListItem(JsonElement item, StringBuilder text, string
bool first = true;
foreach (JsonElement child in Children(item))
{
if (Text(child, "type") is "bulletList" or "orderedList")
if (StringAt(child, "type") is "bulletList" or "orderedList")
{
AppendBlock(child, text, marker);
}
Expand All @@ -162,12 +163,12 @@ private static void AppendListItem(JsonElement item, StringBuilder text, string
}
}

private static string Inline(JsonElement node) => Text(node, "type") switch
private static string Inline(JsonElement node) => StringAt(node, "type") switch
{
"text" => Text(node, "text") ?? string.Empty,
"text" => StringAt(node, "text") ?? string.Empty,
"hardBreak" => "\n",
"mention" or "emoji" => node.TryGetProperty("attrs", out JsonElement attrs) ? Text(attrs, "text") ?? string.Empty : string.Empty,
"inlineCard" => node.TryGetProperty("attrs", out JsonElement attrs) ? Text(attrs, "url") ?? string.Empty : string.Empty,
"mention" or "emoji" => node.TryGetProperty("attrs", out JsonElement attrs) ? StringAt(attrs, "text") ?? string.Empty : string.Empty,
"inlineCard" => node.TryGetProperty("attrs", out JsonElement attrs) ? StringAt(attrs, "url") ?? string.Empty : string.Empty,
_ => string.Concat(Children(node).Select(Inline)),
};

Expand All @@ -176,17 +177,10 @@ private static JsonElement[] Children(JsonElement node)
? [.. content.EnumerateArray()]
: [];

private static string? Text(JsonElement element, string property)
=> element.ValueKind == JsonValueKind.Object && element.TryGetProperty(property, out JsonElement value) && value.ValueKind == JsonValueKind.String
? value.GetString()
: null;

private static string? Text(JsonElement element, string property, string nested)
=> element.ValueKind == JsonValueKind.Object && element.TryGetProperty(property, out JsonElement value) ? Text(value, nested) : null;

private static ImmutableList<string> Names(JsonElement fields, string property)
=> fields.TryGetProperty(property, out JsonElement values) && values.ValueKind == JsonValueKind.Array
? [.. values.EnumerateArray().Select(value => Text(value, "name")).OfType<string>()]
? [.. values.EnumerateArray().Select(value => StringAt(value, "name")).OfType<string>()]
: [];

private static ImmutableList<string> Strings(JsonElement fields, string property)
Expand Down
22 changes: 22 additions & 0 deletions PanoramicData.IssueAssessment/Mapping/JsonReading.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace PanoramicData.IssueAssessment.Mapping;

/// <summary>
/// Reads values out of the JSON a platform returns, tolerating anything of the wrong shape as absent.
/// </summary>
internal static class JsonReading
{
/// <summary>
/// The string at <paramref name="property"/> of an object; null when it is not an object, the property is missing or
/// the value is not a string.
/// </summary>
public static string? StringAt(JsonElement element, string property)
=> element.ValueKind == JsonValueKind.Object && element.TryGetProperty(property, out JsonElement value) && value.ValueKind == JsonValueKind.String
? value.GetString()
: null;

/// <summary>
/// The string at <paramref name="nested"/> of the object at <paramref name="property"/>.
/// </summary>
public static string? StringAt(JsonElement element, string property, string nested)
=> element.ValueKind == JsonValueKind.Object && element.TryGetProperty(property, out JsonElement value) ? StringAt(value, nested) : null;
}
20 changes: 13 additions & 7 deletions PanoramicData.IssueAssessment/Text/IssueText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,23 +186,29 @@ private static void Classify(string? heading, List<string> body, List<string> pa

string? first = body.Find(line => line.Trim().Length > 0);
Match panel = first is null ? Match.Empty : PanelOpening.Match(first);
bool hasContent = first is not null && !IsPlaceholder(string.Join('\n', body));
bool isPlain = !body.Exists(line => PanelBlocker.IsMatch(line));
if (panel.Success)
{
panelable.Add(heading);
if (panel.Groups[1].Value.Contains("title=", StringComparison.OrdinalIgnoreCase))
{
titled.Add(heading);
}
AddIf(titled, heading, panel.Groups[1].Value.Contains("title=", StringComparison.OrdinalIgnoreCase));
return;
}
else if (hasContent && isPlain)

bool hasContent = first is not null && !IsPlaceholder(string.Join('\n', body));
if (hasContent && !body.Exists(line => PanelBlocker.IsMatch(line)))
{
panelable.Add(heading);
unpanelled.Add(heading);
}
}

private static void AddIf(List<string> list, string item, bool condition)
{
if (condition)
{
list.Add(item);
}
}

/// <summary>
/// True when a section body is a placeholder (TBD, N/A, none, to do, pending, ...) or blank, rather than content.
/// </summary>
Expand Down
Loading