using Microsoft.Extensions.Logging; using System.Collections.ObjectModel; using System.Text.Json; using System.Text.Json.Serialization; namespace File_Folder_Helper.Day.Q32024; internal static partial class Helper20240911 { public record Attribute([property: JsonPropertyName("isLocked")] bool IsLocked, [property: JsonPropertyName("name")] string Name); public record Relation([property: JsonPropertyName("rel")] string Type, [property: JsonPropertyName("url")] string URL, [property: JsonPropertyName("attributes")] Attribute Attribute); public record Record(WorkItem WorkItem, ReadOnlyDictionary Children); public record WorkItem(string AreaPath, string? AssignedTo, int? BusinessValue, DateTime ChangedDate, DateTime? ClosedDate, int CommentCount, DateTime CreatedDate, string Description, float? Effort, int Id, string IterationPath, int? Parent, int? Priority, Relation[] Relations, string? Requester, DateTime? ResolvedDate, int Revision, int? RiskReductionMinusOpportunityEnablement, DateTime? StartDate, string State, string Tags, DateTime? TargetDate, float? TimeCriticality, string Title, string WorkItemType, float? WeightedShortestJobFirst); [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(WorkItem))] internal partial class WorkItemSourceGenerationContext : JsonSerializerContext { } private static ReadOnlyDictionary GetWorkItems(string filterDirectory, string[] files) { Dictionary results = []; string json; WorkItem? workItem; string? directoryName; foreach (string file in files) { directoryName = Path.GetDirectoryName(file); if (string.IsNullOrEmpty(directoryName)) continue; if (!directoryName.EndsWith(filterDirectory)) continue; json = File.ReadAllText(file); workItem = JsonSerializer.Deserialize(json, WorkItemSourceGenerationContext.Default.WorkItem); if (workItem is null) continue; results.Add(workItem.Id, workItem); } return new(results); } private static int? GetIdFromUrlIfChild(Relation relation) { int? result; string[] segments = relation.Attribute.Name != "Child" ? [] : relation.URL.Split('/'); if (segments.Length < 2) result = null; else { if (!int.TryParse(segments[^1], out int id)) result = null; else result = id; } return result; } private static Dictionary GetKeyValuePairs(ReadOnlyDictionary workItems, WorkItem workItem) { Dictionary results = []; int? childId; WorkItem? childWorkItem; Dictionary keyValuePairs; if (workItem.Relations is not null && workItem.Relations.Length > 0) { foreach (Relation relation in workItem.Relations) { childId = GetIdFromUrlIfChild(relation); if (childId is null || !workItems.TryGetValue(childId.Value, out childWorkItem)) continue; keyValuePairs = GetKeyValuePairs(workItems, childWorkItem); results.Add(childId.Value, new(childWorkItem, new(keyValuePairs))); } } return results; } private static ReadOnlyDictionary GetWorkItemAndChildren(ReadOnlyDictionary workItems) { Dictionary results = []; Dictionary keyValuePairs; foreach (KeyValuePair keyValuePair in workItems) { // if (keyValuePair.Key != 119185) // continue; keyValuePairs = GetKeyValuePairs(workItems, keyValuePair.Value); results.Add(keyValuePair.Key, new(keyValuePair.Value, new(keyValuePairs))); } return new(results); } private static string GetClosed(WorkItem workItem) { string result = workItem.ClosedDate is null ? "[ ]" : "[x]"; return result; } private static void AppendLines(List spaces, List lines, Record record) { spaces.Add('\t'); WorkItem workItem; foreach (KeyValuePair keyValuePair in record.Children) { workItem = keyValuePair.Value.WorkItem; lines.Add($"{new string(spaces.ToArray())}- {GetClosed(workItem)} {keyValuePair.Key} - {workItem.Title} - {workItem.ClosedDate}"); AppendLines(spaces, lines, keyValuePair.Value); } spaces.RemoveAt(0); } private static void AppendLines(List spaces, List lines, ReadOnlyDictionary workItemAndChildren) { WorkItem workItem; foreach (KeyValuePair keyValuePair in workItemAndChildren) { workItem = keyValuePair.Value.WorkItem; lines.Add($"## {keyValuePair.Key} - {workItem.Title}"); lines.Add(string.Empty); lines.Add($"{new string(spaces.ToArray())}- {GetClosed(workItem)} {keyValuePair.Key} - {workItem.Title} - {workItem.ClosedDate}"); AppendLines(spaces, lines, keyValuePair.Value); lines.Add(string.Empty); } } internal static void WriteMarkdown(ILogger logger, List args) { List spaces = []; string searchPattern = args[2]; string filterDirectory = args[3]; List lines = ["# WorkItems", string.Empty]; string sourceDirectory = Path.GetFullPath(args[0]); string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories); logger.LogInformation("With search pattern '{SearchPattern}' found {files} file(s)", searchPattern, files.Length); ReadOnlyDictionary workItems = GetWorkItems(filterDirectory, files); logger.LogInformation("With search pattern '{SearchPattern}' found {files} workItem(s)", searchPattern, workItems.Count); ReadOnlyDictionary workItemAndChildren = GetWorkItemAndChildren(workItems); logger.LogInformation("With search pattern '{SearchPattern}' found {files} workItemAndChildren", searchPattern, workItemAndChildren.Count); if (workItemAndChildren.Count == -1) { string json = JsonSerializer.Serialize(workItemAndChildren, new JsonSerializerOptions() { WriteIndented = true }); File.WriteAllText(".json", json); } AppendLines(spaces, lines, workItemAndChildren); File.WriteAllText(".md", string.Join(Environment.NewLine, lines)); } }