file-folder-helper/ADO2024/PI4/Helper-2024-11-08.cs
2024-11-08 17:59:42 -07:00

393 lines
19 KiB
C#

using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI4;
internal static partial class Helper20241108
{
private record Attribute([property: JsonPropertyName("isLocked")] bool IsLocked,
[property: JsonPropertyName("name")] string Name);
private record Relation([property: JsonPropertyName("rel")] string Type,
[property: JsonPropertyName("url")] string URL,
[property: JsonPropertyName("attributes")] Attribute Attributes);
private record WorkItem(DateTime? ActivatedDate,
string AreaPath,
string? AssignedTo,
long? BusinessValue,
DateTime ChangedDate,
DateTime? ClosedDate,
int CommentCount,
DateTime CreatedDate,
string Description,
long? Effort,
int Id,
string IterationPath,
int? Parent,
int? Priority,
Relation[]? Relations,
string? Requester,
DateTime? ResolvedDate,
int Revision,
long? RiskReductionMinusOpportunityEnablement,
DateTime? StartDate,
string State,
string Tags,
DateTime? TargetDate,
long? TimeCriticality,
string Title,
string? Violation,
long? WeightedShortestJobFirst,
string WorkItemType)
{
public override string ToString() => $"{Id} - {WorkItemType} - {Title}";
public static WorkItem? GetWithOutRelations(WorkItem? workItem)
{
WorkItem? result = workItem is null ? null : new(workItem.ActivatedDate,
workItem.AreaPath,
workItem.AssignedTo,
workItem.BusinessValue,
workItem.ChangedDate,
workItem.ClosedDate,
workItem.CommentCount,
workItem.CreatedDate,
workItem.Description,
workItem.Effort,
workItem.Id,
workItem.IterationPath,
workItem.Parent,
workItem.Priority,
Array.Empty<Relation>(),
workItem.Requester,
workItem.ResolvedDate,
workItem.Revision,
workItem.RiskReductionMinusOpportunityEnablement,
workItem.StartDate,
workItem.State,
workItem.Tags,
workItem.TargetDate,
workItem.TimeCriticality,
workItem.Title,
workItem.Violation,
workItem.WeightedShortestJobFirst,
workItem.WorkItemType);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WorkItem))]
private partial class WorkItemSourceGenerationContext : JsonSerializerContext
{
}
private record Record(WorkItem WorkItem, WorkItem? Parent, Record[] Children, Record[] Related, Record[] Successors)
{
internal static Record GetWithoutNesting(Record record, string? violation)
{
Record result;
WorkItem workItem = new(record.WorkItem.ActivatedDate,
record.WorkItem.AreaPath,
record.WorkItem.AssignedTo,
record.WorkItem.BusinessValue,
record.WorkItem.ChangedDate,
record.WorkItem.ClosedDate,
record.WorkItem.CommentCount,
record.WorkItem.CreatedDate,
record.WorkItem.Description,
record.WorkItem.Effort,
record.WorkItem.Id,
record.WorkItem.IterationPath,
record.WorkItem.Parent,
record.WorkItem.Priority,
record.WorkItem.Relations,
record.WorkItem.Requester,
record.WorkItem.ResolvedDate,
record.WorkItem.Revision,
record.WorkItem.RiskReductionMinusOpportunityEnablement,
record.WorkItem.StartDate,
record.WorkItem.State,
record.WorkItem.Tags,
record.WorkItem.TargetDate,
record.WorkItem.TimeCriticality,
record.WorkItem.Title,
record.WorkItem.Violation is null ? violation : record.WorkItem.Violation,
record.WorkItem.WeightedShortestJobFirst,
record.WorkItem.WorkItemType);
result = new(workItem, record.Parent, [], [], []);
return result;
}
private static Record Get(Record record, bool keepRelations)
{
Record result;
WorkItem? parentWorkItem = keepRelations ? record.Parent : WorkItem.GetWithOutRelations(record.Parent);
WorkItem? workItem = keepRelations ? record.WorkItem : WorkItem.GetWithOutRelations(record.WorkItem) ?? throw new Exception();
List<Record> childRecords = [];
List<Record> relatedRecords = [];
List<Record> successorRecords = [];
foreach (Record r in record.Children)
childRecords.Add(Get(r, keepRelations));
foreach (Record r in record.Related)
relatedRecords.Add(Get(r, keepRelations));
foreach (Record r in record.Successors)
successorRecords.Add(Get(r, keepRelations));
result = new(workItem, parentWorkItem, childRecords.ToArray(), relatedRecords.ToArray(), successorRecords.ToArray());
return result;
}
internal static Record Get(WorkItem workItem, WorkItem? parent, ReadOnlyCollection<Record> children, ReadOnlyCollection<Record> related, ReadOnlyCollection<Record> successors, bool keepRelations)
{
Record result;
Record record = new(workItem, parent, children.ToArray(), related.ToArray(), successors.ToArray());
result = Get(record, keepRelations);
return result;
}
private static int? GetIdFromUrl(string relationName, Relation relation)
{
int? result;
string[] segments = relation?.Attributes is null || relation.Attributes.Name != relationName ? [] : 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;
}
internal static ReadOnlyCollection<Record> GetKeyValuePairs(ReadOnlyDictionary<int, WorkItem> keyValuePairs, WorkItem workItem, string relationName, List<bool> nests, bool keepRelations)
{
List<Record> results = [];
int? childId;
Record record;
nests.Add(true);
WorkItem? childWorkItem;
WorkItem? parentWorkItem;
List<WorkItem> collection = [];
ReadOnlyCollection<Record> childRecords;
ReadOnlyCollection<Record> relatedRecords;
ReadOnlyCollection<Record> successorRecords;
if (workItem.Relations is not null && workItem.Relations.Length > 0)
{
collection.Clear();
foreach (Relation relation in workItem.Relations)
{
childId = GetIdFromUrl(relationName, relation);
if (childId is not null && workItem.Parent is not null && relation?.URL is not null && relation.URL.Contains(workItem.Parent.Value.ToString()))
continue;
if (childId is null || !keyValuePairs.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 w in collection)
{
if (nests.Count > 99)
break;
if (w.Parent is null)
parentWorkItem = null;
else
_ = keyValuePairs.TryGetValue(w.Parent.Value, out parentWorkItem);
childRecords = GetKeyValuePairs(keyValuePairs, w, "Child", nests, keepRelations); // Forward
// records = GetKeyValuePairs(keyValuePairs, w, "Predecessor", nests); // Reverse
// successorRecords = GetKeyValuePairs(keyValuePairs, w, "Successor", nests); // Forward
// if (successorRecords.Count > 0)
// {
// if (successorRecords.Count > 0)
// { }
// }
successorRecords = new([]);
relatedRecords = GetKeyValuePairs(keyValuePairs, w, "Related", nests, keepRelations); // Related
record = Get(w, parentWorkItem, childRecords, relatedRecords, successorRecords, keepRelations);
results.Add(record);
}
}
return new(results);
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Record[]))]
private partial class RecordCollectionBCommonSourceGenerationContext : JsonSerializerContext
{
}
private static ReadOnlyDictionary<int, Record> GetKeyValuePairs(ReadOnlyDictionary<int, WorkItem> keyValuePairs, bool keepRelations)
{
Dictionary<int, Record> results = [];
Record record;
List<bool> nests = [];
WorkItem? parentWorkItem;
ReadOnlyCollection<Record> childRecords;
ReadOnlyCollection<Record> relatedRecords;
ReadOnlyCollection<Record> successorRecords;
foreach (KeyValuePair<int, WorkItem> keyValuePair in keyValuePairs)
{
nests.Clear();
if (keyValuePair.Value.Parent is null)
parentWorkItem = null;
else
_ = keyValuePairs.TryGetValue(keyValuePair.Value.Parent.Value, out parentWorkItem);
try
{
childRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Child", nests, keepRelations); // Forward
// records = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Predecessor", nests, keepRelations); // Reverse
relatedRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Related", nests, keepRelations); // Related
successorRecords = Record.GetKeyValuePairs(keyValuePairs, keyValuePair.Value, "Successor", nests, keepRelations); // Forward
record = Record.Get(keyValuePair.Value, parentWorkItem, childRecords, relatedRecords, successorRecords, keepRelations);
}
catch (Exception)
{
record = new(keyValuePair.Value, parentWorkItem, [], [], []);
}
results.Add(keyValuePair.Key, record);
}
return new(results);
}
private static ReadOnlyDictionary<int, Record> GetWorkItems(ReadOnlyCollection<WorkItem> workItems, bool keepRelations)
{
ReadOnlyDictionary<int, Record> results;
Dictionary<int, WorkItem> keyValuePairs = [];
foreach (WorkItem workItem in workItems)
keyValuePairs.Add(workItem.Id, workItem);
results = GetKeyValuePairs(new(keyValuePairs), keepRelations);
return results;
}
private static ReadOnlyDictionary<int, string> GetCurrentDirectories(string destinationDirectory, ReadOnlyCollection<string> bugUserStoryWorkItemTypes)
{
Dictionary<int, string> results = [];
int id;
string idCheck;
string? fileName;
string[] directories;
string[] split = ["-"];
foreach (string w in bugUserStoryWorkItemTypes)
{
directories = Directory.GetDirectories(destinationDirectory, $"*-{w.Replace(" ", "-")}", SearchOption.AllDirectories);
foreach (string directory in directories)
{
fileName = Path.GetFileName(directory);
if (string.IsNullOrEmpty(fileName))
continue;
idCheck = fileName.Split(split, StringSplitOptions.None)[0];
if (!int.TryParse(idCheck, out id))
continue;
if (!results.ContainsKey(id))
results.Add(id, directory);
else
MoveToDuplicate(destinationDirectory, directory);
}
}
return new(results);
}
private static void MoveToDuplicate(string destinationDirectory, string directory)
{
if (string.IsNullOrEmpty(destinationDirectory))
throw new ArgumentException($"'{nameof(destinationDirectory)}' cannot be null or empty.", nameof(destinationDirectory));
if (string.IsNullOrEmpty(directory))
throw new ArgumentException($"'{nameof(directory)}' cannot be null or empty.", nameof(directory));
}
private static ReadOnlyCollection<WorkItem> GetWorkItems(string sourceDirectory, ReadOnlyCollection<string> bugUserStoryWorkItemTypes)
{
List<WorkItem> results = [];
string json;
string checkFile;
WorkItem? workItem;
string directoryName;
string? checkDirectory = null;
foreach (string w in bugUserStoryWorkItemTypes)
{
directoryName = Path.GetFileName(sourceDirectory);
if (!directoryName.EndsWith(w.Replace(" ", "-")))
continue;
checkDirectory = Path.Combine(sourceDirectory, directoryName.Split('-')[0]);
if (!Directory.Exists(checkDirectory))
{
checkDirectory = null;
continue;
}
break;
}
if (!string.IsNullOrEmpty(checkDirectory))
{
checkFile = Path.Combine(checkDirectory, ".json");
if (File.Exists(checkFile))
{
json = File.ReadAllText(checkFile);
workItem = JsonSerializer.Deserialize(json, WorkItemSourceGenerationContext.Default.WorkItem);
if (workItem is not null)
results.Add(workItem);
}
}
return new(results);
}
private static ReadOnlyCollection<WorkItem> GetWorkItems(ReadOnlyDictionary<int, string> collection)
{
List<WorkItem> results = [];
string json;
string checkFile;
string? directory;
WorkItem? workItem;
string? checkDirectory;
foreach (KeyValuePair<int, string> keyValuePair in collection)
{
if (!collection.TryGetValue(keyValuePair.Key, out directory) || string.IsNullOrEmpty(directory))
continue;
checkDirectory = Path.Combine(directory, $"{keyValuePair.Key}");
if (!Directory.Exists(checkDirectory))
continue;
checkFile = Path.Combine(checkDirectory, ".json");
if (!File.Exists(checkFile))
continue;
json = File.ReadAllText(checkFile);
workItem = JsonSerializer.Deserialize(json, WorkItemSourceGenerationContext.Default.WorkItem);
if (workItem is null)
continue;
results.Add(workItem);
}
return new(results);
}
private static ReadOnlyCollection<WorkItem> GetWorkItems(string sourceDirectory, ReadOnlyCollection<string> bugUserStoryWorkItemTypes, ReadOnlyDictionary<int, string> collection)
{
ReadOnlyCollection<WorkItem> results;
if (collection.Count > 0)
results = GetWorkItems(collection);
else
results = GetWorkItems(sourceDirectory, bugUserStoryWorkItemTypes);
return results;
}
internal static void WriteMarkdown(ILogger<Worker> logger, List<string> args)
{
// string url = args[5];
bool keepRelations = true;
string sourceDirectory = Path.GetFullPath(args[0]);
ReadOnlyCollection<string> bugUserStoryWorkItemTypes = args[2].Split('|').ToArray().AsReadOnly();
// ReadOnlyCollection<string> bugUserStoryTaskWorkItemTypes = new(new string[] { "Bug", "User Story", "Task" });
ReadOnlyDictionary<int, string> collection = GetCurrentDirectories(sourceDirectory, bugUserStoryWorkItemTypes);
ReadOnlyCollection<WorkItem> workItems = GetWorkItems(sourceDirectory, bugUserStoryWorkItemTypes, collection);
ReadOnlyDictionary<int, Record> keyValuePairs = GetWorkItems(workItems, keepRelations);
logger.LogInformation("Found {keyValuePairs}", keyValuePairs.Count);
}
}