384 lines
18 KiB
C#
384 lines
18 KiB
C#
using Microsoft.Extensions.Logging;
|
|
#if WorkItems
|
|
using System.Collections.ObjectModel;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
#endif
|
|
|
|
namespace File_Folder_Helper.ADO2024.PI4;
|
|
|
|
internal static partial class Helper20241108
|
|
{
|
|
|
|
#if WorkItems
|
|
|
|
private record Attribute([property: JsonPropertyName("isLocked")] bool IsLocked,
|
|
[property: JsonPropertyName("name")] string Name,
|
|
[property: JsonPropertyName("parameterTitle")] string? ParameterTitle,
|
|
[property: JsonPropertyName("state")] string? State,
|
|
[property: JsonPropertyName("workItemType")] string? WorkItemType);
|
|
|
|
private record Relation([property: JsonPropertyName("attributes")] Attribute Attributes,
|
|
[property: JsonPropertyName("id")] int Id,
|
|
[property: JsonPropertyName("rel")] string Rel);
|
|
|
|
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 Get(WorkItem workItem, Relation[] relations)
|
|
{
|
|
WorkItem result = 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,
|
|
relations,
|
|
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;
|
|
}
|
|
|
|
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
|
|
{
|
|
}
|
|
|
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
|
[JsonSerializable(typeof(WorkItem[]))]
|
|
private partial class WorkItemCollectionSourceGenerationContext : 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, null, null, null);
|
|
return result;
|
|
}
|
|
|
|
private static Record Get(Record record, bool keepRelations)
|
|
{
|
|
Record result;
|
|
Record[]? childRecords;
|
|
Record[]? relatedRecords;
|
|
Record[]? successorRecords;
|
|
List<Record> relationRecords;
|
|
WorkItem? parentWorkItem = keepRelations ? record.Parent : WorkItem.GetWithOutRelations(record.Parent);
|
|
WorkItem? workItem = keepRelations ? record.WorkItem : WorkItem.GetWithOutRelations(record.WorkItem) ?? throw new Exception();
|
|
if (record.Children is null)
|
|
childRecords = null;
|
|
else
|
|
{
|
|
relationRecords = [];
|
|
foreach (Record r in record.Children)
|
|
relationRecords.Add(Get(r, keepRelations));
|
|
childRecords = relationRecords.ToArray();
|
|
}
|
|
if (record.Related is null)
|
|
relatedRecords = null;
|
|
else
|
|
{
|
|
relationRecords = [];
|
|
foreach (Record r in record.Related)
|
|
relationRecords.Add(Get(r, keepRelations));
|
|
relatedRecords = relationRecords.ToArray();
|
|
}
|
|
if (record.Successors is null)
|
|
successorRecords = null;
|
|
else
|
|
{
|
|
relationRecords = [];
|
|
foreach (Record r in record.Successors)
|
|
relationRecords.Add(Get(r, keepRelations));
|
|
successorRecords = relationRecords.ToArray();
|
|
}
|
|
result = new(workItem, parentWorkItem, childRecords, relatedRecords, successorRecords);
|
|
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;
|
|
}
|
|
|
|
internal static ReadOnlyCollection<Record> GetKeyValuePairs(ReadOnlyDictionary<int, WorkItem> keyValuePairs, WorkItem workItem, string relationName, List<bool> nests, bool keepRelations)
|
|
{
|
|
List<Record> results = [];
|
|
Record record;
|
|
nests.Add(true);
|
|
WorkItem? parentWorkItem;
|
|
WorkItem? relationWorkItem;
|
|
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)
|
|
{
|
|
if (relation.Attributes.Name != relationName)
|
|
continue;
|
|
if (workItem.Parent is not null && relation.Id == workItem.Parent.Value)
|
|
continue;
|
|
if (!keyValuePairs.TryGetValue(relation.Id, out relationWorkItem))
|
|
continue;
|
|
collection.Add(relationWorkItem);
|
|
}
|
|
collection = (from l in collection orderby l.State != "Closed", l.Id select l).ToList();
|
|
foreach (WorkItem w in collection)
|
|
{
|
|
if (nests.Count > 500)
|
|
break;
|
|
if (w.Parent is null)
|
|
parentWorkItem = null;
|
|
else
|
|
_ = keyValuePairs.TryGetValue(w.Parent.Value, out parentWorkItem);
|
|
childRecords = GetKeyValuePairs(keyValuePairs, w, "Child", nests, keepRelations); // Forward
|
|
relatedRecords = null; // GetKeyValuePairs(keyValuePairs, w, "Related", nests, keepRelations); // Related
|
|
successorRecords = null; // GetKeyValuePairs(keyValuePairs, w, "Successor", nests, keepRelations); // Forward
|
|
// predecessorRecords = GetKeyValuePairs(keyValuePairs, w, "Predecessor", nests, keepRelations); // Reverse
|
|
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 ReadOnlyCollection<WorkItem>? GetWorkItems(string fileName, string sourceDirectory)
|
|
{
|
|
WorkItem[]? results;
|
|
string? checkFile = null;
|
|
string? checkDirectory = sourceDirectory;
|
|
string? pathRoot = Path.GetPathRoot(sourceDirectory);
|
|
for (int i = 0; i < int.MaxValue; i++)
|
|
{
|
|
checkDirectory = Path.GetDirectoryName(checkDirectory);
|
|
if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot)
|
|
break;
|
|
checkFile = Path.Combine(checkDirectory, fileName);
|
|
if (File.Exists(checkFile))
|
|
break;
|
|
checkFile = null;
|
|
}
|
|
if (checkFile is null)
|
|
results = null;
|
|
else
|
|
{
|
|
string json = File.ReadAllText(checkFile);
|
|
results = JsonSerializer.Deserialize(json, WorkItemCollectionSourceGenerationContext.Default.WorkItemArray);
|
|
}
|
|
return results is null ? null : new(results);
|
|
}
|
|
|
|
internal static void WriteMarkdown(ILogger<Worker> logger, List<string> args)
|
|
{
|
|
// string url = args[5];
|
|
bool keepRelations = true;
|
|
string fileName = args[2];
|
|
string sourceDirectory = Path.GetFullPath(args[0]);
|
|
string sourceDirectoryName = Path.GetFileName(sourceDirectory);
|
|
string idCheck = sourceDirectoryName.Split('-', StringSplitOptions.None)[0];
|
|
if (!int.TryParse(idCheck, out int id))
|
|
logger.LogInformation("Not valid directory!");
|
|
else
|
|
{
|
|
ReadOnlyCollection<WorkItem>? workItems = GetWorkItems(fileName, sourceDirectory);
|
|
if (workItems is null)
|
|
logger.LogInformation("No file found!");
|
|
else
|
|
{
|
|
Record? record;
|
|
ReadOnlyDictionary<int, Record> keyValuePairs = GetWorkItems(workItems, keepRelations);
|
|
logger.LogInformation("Found {keyValuePairs}", keyValuePairs.Count);
|
|
if (!keyValuePairs.TryGetValue(id, out record))
|
|
logger.LogInformation($"Id {id} not found!");
|
|
else
|
|
{
|
|
logger.LogInformation($"Id {id} found with title {record.WorkItem.Title}!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
internal static void WriteMarkdown(ILogger<Worker> logger, List<string> args)
|
|
{
|
|
logger.LogError("WriteMarkdown is not available in WorkItems {args[0]}", args[0]);
|
|
logger.LogError("WriteMarkdown is not available in WorkItems {args[1]}", args[1]);
|
|
}
|
|
|
|
#endif
|
|
|
|
} |