From 513e8ae4fc5348a66c546fb8991a1b49915e5c5d Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Fri, 13 Sep 2024 14:14:22 -0700 Subject: [PATCH] Children --- Adaptation/FileHandlers/json/FileRead.cs | 167 +++++++++++++++++- .../FileHandlers/json/WorkItems/Attribute.cs | 21 +++ .../FileHandlers/json/WorkItems/Record.cs | 19 ++ .../FileHandlers/json/WorkItems/Relation.cs | 24 +++ .../FileHandlers/json/WorkItems/Value.cs | 4 +- .../FileHandlers/json/WorkItems/WorkItem.cs | 4 +- MESAFIBACKLOG.csproj | 3 + 7 files changed, 234 insertions(+), 8 deletions(-) create mode 100644 Adaptation/FileHandlers/json/WorkItems/Attribute.cs create mode 100644 Adaptation/FileHandlers/json/WorkItems/Record.cs create mode 100644 Adaptation/FileHandlers/json/WorkItems/Relation.cs diff --git a/Adaptation/FileHandlers/json/FileRead.cs b/Adaptation/FileHandlers/json/FileRead.cs index 8fc2383..ddc05d9 100644 --- a/Adaptation/FileHandlers/json/FileRead.cs +++ b/Adaptation/FileHandlers/json/FileRead.cs @@ -19,6 +19,7 @@ public class FileRead : Shared.FileRead, IFileRead { private readonly Timer _Timer; + private readonly ReadOnlyCollection _WorkItemTypes; public FileRead(ISMTP smtp, Dictionary fileParameter, string cellInstanceName, int? connectionCount, string cellInstanceConnectionName, FileConnectorConfiguration fileConnectorConfiguration, string equipmentTypeName, string parameterizedModelObjectDefinitionType, IList modelObjectParameters, string equipmentDictionaryName, Dictionary> dummyRuns, Dictionary> staticRuns, bool useCyclicalForDescription, bool isEAFHosted) : base(new Description(), false, smtp, fileParameter, cellInstanceName, connectionCount, cellInstanceConnectionName, fileConnectorConfiguration, equipmentTypeName, parameterizedModelObjectDefinitionType, modelObjectParameters, equipmentDictionaryName, dummyRuns, staticRuns, useCyclicalForDescription, isEAFHosted: connectionCount is null) @@ -32,6 +33,9 @@ public class FileRead : Shared.FileRead, IFileRead throw new Exception(cellInstanceConnectionName); if (_IsDuplicator) throw new Exception(cellInstanceConnectionName); + string cellInstanceNamed = string.Concat("CellInstance.", _EquipmentType); + string workItemTypes = GetPropertyValue(cellInstanceConnectionName, modelObjectParameters, $"{cellInstanceNamed}.WorkItemTypes"); + _WorkItemTypes = new(workItemTypes.Split('|')); if (!Debugger.IsAttached && fileConnectorConfiguration.PreProcessingMode != FileConnectorConfiguration.PreProcessingModeEnum.Process) _Timer = new Timer(Callback, null, (int)(fileConnectorConfiguration.FileScanningIntervalInSeconds * 1000), Timeout.Infinite); else @@ -119,6 +123,8 @@ public class FileRead : Shared.FileRead, IFileRead if (alternateTargetFolder == fileConnectorConfiguration.TargetFileLocation) continue; pathRoot = Path.GetPathRoot(alternateTargetFolder); + if (string.IsNullOrEmpty(pathRoot)) + continue; try { if (!Directory.Exists(pathRoot)) @@ -194,7 +200,157 @@ public class FileRead : Shared.FileRead, IFileRead return new(results); } - private static void ParseWorkItemsAsync(FileConnectorConfiguration fileConnectorConfiguration, string[] alternateTargetFolders) + private static ReadOnlyDictionary GetWorkItems(ReadOnlyCollection workItems) + { + Dictionary results = new(); + foreach (WorkItem workItem in workItems) + results.Add(workItem.Id, workItem); + return new(results); + } + + private static int? GetIdFromUrlIfChild(Relation relation) + { + int? result; + string[] segments = relation.Attributes.Name != "Child" ? Array.Empty() : relation.URL.Split('/'); + if (segments.Length < 2) + result = null; + else + { + if (!int.TryParse(segments[segments.Length - 1], out int id)) + result = null; + else + result = id; + } + return result; + } + + private static Dictionary GetKeyValuePairs(ReadOnlyDictionary workItems, WorkItem workItem) + { + Dictionary results = new(); + int? childId; + WorkItem? childWorkItem; + List collection = new(); + Dictionary keyValuePairs; + if (workItem.Relations is not null && workItem.Relations.Length > 0) + { + collection.Clear(); + foreach (Relation relation in workItem.Relations) + { + childId = GetIdFromUrlIfChild(relation); + if (childId is null || !workItems.TryGetValue(childId.Value, out childWorkItem)) + continue; + collection.Add(childWorkItem); + } + collection = (from l in collection orderby l.State != "Closed", l.Id select l).ToList(); + foreach (WorkItem item in collection) + { + keyValuePairs = GetKeyValuePairs(workItems, item); + results.Add(item.Id, new(item, new(keyValuePairs))); + } + } + return results; + } + + private static ReadOnlyDictionary GetWorkItemAndChildren(ReadOnlyDictionary workItems) + { + Dictionary results = new(); + 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.State != "Closed" ? "[ ]" : "[x]"; + return result; + } + + private static string GetLine(List spaces, WorkItem workItem, KeyValuePair keyValuePair, bool condensed) => + !condensed ? $"{new string(spaces.Skip(1).ToArray())}- {GetClosed(workItem)} {keyValuePair.Key} - {workItem.Title}" : + $"{new string(spaces.Skip(1).ToArray())}- {GetClosed(workItem)} {keyValuePair.Key} - {workItem.Title} ~~~ {workItem.AssignedTo} - {workItem.IterationPath.Replace('\\', '-')} - {workItem.CreatedDate} --- {workItem.ClosedDate}"; + + private static void AppendLines(List spaces, List lines, Record record, bool condensed) + { + spaces.Add('\t'); + WorkItem workItem; + foreach (KeyValuePair keyValuePair in record.Children) + { + workItem = keyValuePair.Value.WorkItem; + lines.Add(GetLine(spaces, workItem, keyValuePair, condensed)); + AppendLines(spaces, lines, keyValuePair.Value, condensed); + } + spaces.RemoveAt(0); + } + + private static void AppendLines(List spaces, List lines, ReadOnlyDictionary workItemAndChildren, string workItemType) + { + WorkItem workItem; + foreach (KeyValuePair keyValuePair in workItemAndChildren) + { + workItem = keyValuePair.Value.WorkItem; + if (workItem.WorkItemType != workItemType) + continue; + lines.Add($"## {workItem.AssignedTo} - {workItem.Id} - {workItem.Title}"); + lines.Add(string.Empty); + lines.Add($"- [{workItem.Id}](https://tfs.intra.infineon.com/tfs/FactoryIntegration/ART%20SPS/_workitems/edit/{workItem.Id})"); + lines.Add(string.Empty); + if (keyValuePair.Value.Children.Count > 0) + { + AppendLines(spaces, lines, keyValuePair.Value, condensed: true); + lines.Add(string.Empty); + lines.Add($"## Extended - {workItem.Id} - {workItem.Title}"); + lines.Add(string.Empty); + AppendLines(spaces, lines, keyValuePair.Value, condensed: false); + lines.Add(string.Empty); + } + } + } + + private static void WriteMarkdownFile(FileConnectorConfiguration fileConnectorConfiguration, string[] alternateTargetFolders, ReadOnlyCollection workItems, ReadOnlyCollection workItemTypes) + { + string json; + string? pathRoot; + List spaces = new(); + List lines = new(); + lines.Add("# WorkItems"); + ReadOnlyDictionary keyValuePairs = GetWorkItems(workItems); + ReadOnlyDictionary workItemAndChildren = GetWorkItemAndChildren(keyValuePairs); + foreach (string alternateTargetFolder in alternateTargetFolders) + { + if (alternateTargetFolder == fileConnectorConfiguration.TargetFileLocation) + continue; + pathRoot = Path.GetPathRoot(alternateTargetFolder); + if (string.IsNullOrEmpty(pathRoot)) + continue; + try + { + if (!Directory.Exists(pathRoot)) + continue; + } + catch (Exception) + { continue; } + if (!Directory.Exists(alternateTargetFolder)) + _ = Directory.CreateDirectory(alternateTargetFolder); + if (workItemAndChildren.Count == -1) + { + json = JsonSerializer.Serialize(workItemAndChildren, new JsonSerializerOptions() { WriteIndented = true }); + File.WriteAllText(Path.Combine(alternateTargetFolder, ".json"), json); + } + foreach (string workItemType in workItemTypes) + { + AppendLines(spaces, lines, workItemAndChildren, workItemType); + File.WriteAllText(Path.Combine(alternateTargetFolder, $"{workItemType}.md"), string.Join(Environment.NewLine, lines)); + } + } + } + + private static void ParseWorkItemsAsync(FileConnectorConfiguration fileConnectorConfiguration, string[] alternateTargetFolders, ReadOnlyCollection workItemTypes) { if (!Directory.Exists(fileConnectorConfiguration.TargetFileLocation)) _ = Directory.CreateDirectory(fileConnectorConfiguration.TargetFileLocation); @@ -202,14 +358,17 @@ public class FileRead : Shared.FileRead, IFileRead ReadOnlyCollection collection = GetWorkItems(files); ReadOnlyCollection workItems = GetWorkItems(collection); if (workItems.Count > 0) + { ParseWorkItemsAsync(fileConnectorConfiguration, alternateTargetFolders, workItems); + WriteMarkdownFile(fileConnectorConfiguration, alternateTargetFolders, workItems, workItemTypes); + } } - private static void ParseWorkItemsAsync(FileConnectorConfiguration fileConnectorConfiguration) + private static void ParseWorkItemsAsync(FileConnectorConfiguration fileConnectorConfiguration, ReadOnlyCollection workItemTypes) { string[] alternateTargetFolders = fileConnectorConfiguration.AlternateTargetFolder.Split('|'); if (alternateTargetFolders.Length > 0) - ParseWorkItemsAsync(fileConnectorConfiguration, alternateTargetFolders); + ParseWorkItemsAsync(fileConnectorConfiguration, alternateTargetFolders, workItemTypes); } private void Callback(object state) @@ -217,7 +376,7 @@ public class FileRead : Shared.FileRead, IFileRead try { if (_IsEAFHosted) - ParseWorkItemsAsync(_FileConnectorConfiguration); + ParseWorkItemsAsync(_FileConnectorConfiguration, _WorkItemTypes); } catch (Exception exception) { diff --git a/Adaptation/FileHandlers/json/WorkItems/Attribute.cs b/Adaptation/FileHandlers/json/WorkItems/Attribute.cs new file mode 100644 index 0000000..bdd3e5f --- /dev/null +++ b/Adaptation/FileHandlers/json/WorkItems/Attribute.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace Adaptation.FileHandlers.json.WorkItems; + +public class Attribute +{ + +#nullable enable + + [JsonConstructor] + public Attribute(bool isLocked, + string name) + { + IsLocked = isLocked; + Name = name; + } + + public bool IsLocked { get; set; } // { init; get; } + public string Name { get; set; } // { init; get; } + +} \ No newline at end of file diff --git a/Adaptation/FileHandlers/json/WorkItems/Record.cs b/Adaptation/FileHandlers/json/WorkItems/Record.cs new file mode 100644 index 0000000..7711541 --- /dev/null +++ b/Adaptation/FileHandlers/json/WorkItems/Record.cs @@ -0,0 +1,19 @@ +using System.Collections.ObjectModel; + +namespace Adaptation.FileHandlers.json.WorkItems; + +public class Record +{ + +#nullable enable + + public Record(WorkItem workItem, ReadOnlyDictionary children) + { + WorkItem = workItem; + Children = children; + } + + public WorkItem WorkItem { get; set; } + public ReadOnlyDictionary Children { get; set; } + +} \ No newline at end of file diff --git a/Adaptation/FileHandlers/json/WorkItems/Relation.cs b/Adaptation/FileHandlers/json/WorkItems/Relation.cs new file mode 100644 index 0000000..d0ff8c1 --- /dev/null +++ b/Adaptation/FileHandlers/json/WorkItems/Relation.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace Adaptation.FileHandlers.json.WorkItems; + +public class Relation +{ + +#nullable enable + + [JsonConstructor] + public Relation(string rel, + string url, + Attribute attributes) + { + Rel = rel; + URL = url; + Attributes = attributes; + } + + public string Rel { get; set; } // { init; get; } + public string URL { get; set; } // { init; get; } + public Attribute Attributes { get; set; } // { init; get; } + +} \ No newline at end of file diff --git a/Adaptation/FileHandlers/json/WorkItems/Value.cs b/Adaptation/FileHandlers/json/WorkItems/Value.cs index 1fabff6..17c66b9 100644 --- a/Adaptation/FileHandlers/json/WorkItems/Value.cs +++ b/Adaptation/FileHandlers/json/WorkItems/Value.cs @@ -9,7 +9,7 @@ public class Value int id, int rev, Fields fields, - object[] relations, + Relation[] relations, CommentVersionRef commentVersionRef, string url ) @@ -32,7 +32,7 @@ public class Value public Fields Fields { get; } [JsonPropertyName("relations")] - public object[] Relations { get; } + public Relation[] Relations { get; } [JsonPropertyName("commentVersionRef")] public CommentVersionRef CommentVersionRef { get; } diff --git a/Adaptation/FileHandlers/json/WorkItems/WorkItem.cs b/Adaptation/FileHandlers/json/WorkItems/WorkItem.cs index 4154248..59d3c99 100644 --- a/Adaptation/FileHandlers/json/WorkItems/WorkItem.cs +++ b/Adaptation/FileHandlers/json/WorkItems/WorkItem.cs @@ -22,7 +22,7 @@ public class WorkItem string iterationPath, int? parent, int? priority, - object[]? relations, + Relation[]? relations, string? requester, DateTime? resolvedDate, int revision, @@ -77,7 +77,7 @@ public class WorkItem public string IterationPath { get; set; } // { init; get; } public int? Parent { get; set; } // { init; get; } public int? Priority { get; set; } // { init; get; } - public object[]? Relations { get; set; } // { init; get; } + public Relation[]? Relations { get; set; } // { init; get; } public string? Requester { get; set; } // { init; get; } public DateTime? ResolvedDate { get; set; } // { init; get; } public int Revision { get; set; } // { init; get; } diff --git a/MESAFIBACKLOG.csproj b/MESAFIBACKLOG.csproj index 8ffe20d..2f646f7 100644 --- a/MESAFIBACKLOG.csproj +++ b/MESAFIBACKLOG.csproj @@ -122,12 +122,15 @@ + + +