Moved to ADO2024 PI#

Ran SortCodeMethods
This commit is contained in:
Mike Phares 2024-10-11 09:15:32 -07:00
parent 3d114918e4
commit b6d8d4c52f
58 changed files with 25064 additions and 1423 deletions

12
.vscode/launch.json vendored
View File

@ -13,13 +13,13 @@
"args": [
"s",
"X",
"D:/5-Other-Small/Kanban-messa010ec/Kanban/Work-Items",
"Day-Helper-2024-09-11",
"*.json",
".kanbn",
"Epic|Feature|User Story",
"L:/DevOps/Mesa_FI/File-Folder-Helper/.vscode/helper/tfs",
"https://tfs.intra.infineon.com/tfs/FactoryIntegration/ART%20SPS/_workitems/edit/"
"Day-Helper-2024-09-11",
"https://eaf-dev.mes.infineon.com/json/work-items.json?v=2024-10-04-08-34&_=1728333679350",
"https://oi-metrology-viewer-prod.mes.infineon.com/json/work-items.json?v=2024-10-07-10-09&_=1728336047608",
"Epic|Feature|User Story",
"https://tfs.intra.infineon.com/tfs/FactoryIntegration/ART%20SPS/_workitems/edit/",
"L:/DevOps/Mesa_FI/File-Folder-Helper/.vscode/helper/tfs"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",

View File

@ -6,7 +6,7 @@ using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace File_Folder_Helper.Day.Q12024;
namespace File_Folder_Helper.ADO2024.PI1;
#pragma warning disable IDE1006, CS8618

View File

@ -5,19 +5,13 @@ using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q12024;
namespace File_Folder_Helper.ADO2024.PI1;
internal static partial class Helper20240106
{
private record Record(string Key, Dictionary<string, string> KeyValuePairs);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Dictionary<string, Dictionary<string, string>>))]
private partial class DictionaryDictionarySourceGenerationContext : JsonSerializerContext
{
}
private static Dictionary<string, Dictionary<string, string>> GetKeyValuePairs(List<Record> collection, bool replaceFound)
{
Dictionary<string, Dictionary<string, string>> results = [];
@ -34,6 +28,27 @@ internal static partial class Helper20240106
return results;
}
private static Dictionary<int, Host> GetHosts(string jsonl)
{
Dictionary<int, Host> results = [];
int id;
string json = $"[{File.ReadAllText(jsonl).Replace("\r\n", ",")}]";
Host[] hosts = JsonSerializer.Deserialize(json, HostSourceGenerationContext.Default.HostArray) ?? throw new NullReferenceException(nameof(json));
foreach (Host host in hosts)
{
if (host.Id is null)
continue;
if (host.Hyphen is not null and nameof(host.Hyphen))
continue;
if (!int.TryParse(host.Id, out id))
throw new NotSupportedException($"{host.Id} is not a number");
if (results.ContainsKey(id))
throw new NotSupportedException($"Id {id} is not unique!");
results.Add(id, host);
}
return results;
}
private static int? GetHeaderLine(string[] lines)
{
int? headerLine = null;
@ -97,27 +112,6 @@ internal static partial class Helper20240106
return results;
}
private static Dictionary<int, Host> GetHosts(string jsonl)
{
Dictionary<int, Host> results = [];
int id;
string json = $"[{File.ReadAllText(jsonl).Replace("\r\n", ",")}]";
Host[] hosts = JsonSerializer.Deserialize(json, HostSourceGenerationContext.Default.HostArray) ?? throw new NullReferenceException(nameof(json));
foreach (Host host in hosts)
{
if (host.Id is null)
continue;
if (host.Hyphen is not null and nameof(host.Hyphen))
continue;
if (!int.TryParse(host.Id, out id))
throw new NotSupportedException($"{host.Id} is not a number");
if (results.ContainsKey(id))
throw new NotSupportedException($"Id {id} is not unique!");
results.Add(id, host);
}
return results;
}
private static ReadOnlyCollection<string> GetIpAddressAndVerify(ILogger<Worker> logger, string key, Dictionary<string, Dictionary<string, string>> keyValuePairs, Dictionary<int, Host> hosts, string filter)
{
List<string> results = [];
@ -188,6 +182,12 @@ internal static partial class Helper20240106
File.AppendAllLines(hostConfFile, lines);
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Dictionary<string, Dictionary<string, string>>))]
private partial class DictionaryDictionarySourceGenerationContext : JsonSerializerContext
{
}
internal static void TextToJson(ILogger<Worker> logger, List<string> args)
{
string json;

View File

@ -1,7 +1,7 @@
using DiscUtils.Iso9660;
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.Day.Q12024;
namespace File_Folder_Helper.ADO2024.PI1;
internal static partial class Helper20240107
{

View File

@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
namespace File_Folder_Helper.Day.Q12024;
namespace File_Folder_Helper.ADO2024.PI1;
internal static partial class Helper20240108
{

View File

@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q12024;
namespace File_Folder_Helper.ADO2024.PI1;
internal static partial class Helper20240129
{

View File

@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System.Globalization;
namespace File_Folder_Helper.Day.Q12024;
namespace File_Folder_Helper.ADO2024.PI1;
internal static partial class Helper20240305
{

View File

@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI1;
internal static partial class Helper20240403
{

View File

@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI1;
internal static partial class Helper20240404
{

View File

@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI1;
internal static partial class Helper20240409
{

View File

@ -2,7 +2,7 @@ using File_Folder_Helper.Helpers;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI1;
internal static partial class Helper20240417
{

View File

@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System.Text;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI1;
internal static partial class Helper20240426
{

View File

@ -4,7 +4,7 @@ using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI1;
internal static partial class Helper20240427
{

View File

@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System.Diagnostics;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240429
{

View File

@ -1,6 +1,6 @@
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240510
{

View File

@ -2,7 +2,7 @@ using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging;
using System.Text.Json;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240513
{

View File

@ -3,7 +3,7 @@ using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240517
{

View File

@ -2,7 +2,7 @@ using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging;
using System.Text.Json;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240518
{

View File

@ -2,7 +2,7 @@ using File_Folder_Helper.Helpers;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240519
{

View File

@ -4,7 +4,7 @@ using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240520
{

View File

@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240623
{

View File

@ -1,6 +1,6 @@
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.Day.Q22024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240624
{

View File

@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240711
{

View File

@ -3,7 +3,7 @@ using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Text.Json;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240718
{

View File

@ -5,7 +5,7 @@ using System.Diagnostics;
using System.Globalization;
using System.Text.Json;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240724
{
@ -25,22 +25,10 @@ internal static partial class Helper20240724
[".txt"],
"D:/Tmp/Phares/TargetFileLocation");
private static DateTime GetFileAgeThresholdDateTime(string fileAgeThreshold)
private static string[] GetValidDays(DateTime fileAgeThresholdDateTime)
{
DateTime result = DateTime.Now;
string[] segments = fileAgeThreshold.Split(':');
for (int i = 0; i < segments.Length; i++)
{
result = i switch
{
0 => result.AddDays(double.Parse(segments[i]) * -1),
1 => result.AddHours(double.Parse(segments[i]) * -1),
2 => result.AddMinutes(double.Parse(segments[i]) * -1),
3 => result.AddSeconds(double.Parse(segments[i]) * -1),
_ => throw new Exception(),
};
}
return result;
DateTime dateTime = DateTime.Now;
return new string[] { dateTime.ToString("yyyy-MM-dd"), fileAgeThresholdDateTime.ToString("yyyy-MM-dd") }.Distinct().ToArray();
}
private static string[] GetValidWeeks(DateTime fileAgeThresholdDateTime)
@ -52,12 +40,6 @@ internal static partial class Helper20240724
return new string[] { weekOfYear, lastWeekOfYear }.Distinct().ToArray();
}
private static string[] GetValidDays(DateTime fileAgeThresholdDateTime)
{
DateTime dateTime = DateTime.Now;
return new string[] { dateTime.ToString("yyyy-MM-dd"), fileAgeThresholdDateTime.ToString("yyyy-MM-dd") }.Distinct().ToArray();
}
private static ReadOnlyCollection<NginxFileSystem> GetDayNginxFileSystemCollection(DateTime fileAgeThresholdDateTime, string week, string day, string dayUrl, NginxFileSystem[] dayNginxFileSystemCollection)
{
List<NginxFileSystem> results = new();
@ -78,6 +60,24 @@ internal static partial class Helper20240724
return results.AsReadOnly();
}
private static DateTime GetFileAgeThresholdDateTime(string fileAgeThreshold)
{
DateTime result = DateTime.Now;
string[] segments = fileAgeThreshold.Split(':');
for (int i = 0; i < segments.Length; i++)
{
result = i switch
{
0 => result.AddDays(double.Parse(segments[i]) * -1),
1 => result.AddHours(double.Parse(segments[i]) * -1),
2 => result.AddMinutes(double.Parse(segments[i]) * -1),
3 => result.AddSeconds(double.Parse(segments[i]) * -1),
_ => throw new Exception(),
};
}
return result;
}
private static ReadOnlyCollection<NginxFileSystem> GetDayNginxFileSystemCollection(DateTime fileAgeThresholdDateTime)
{
#nullable enable

View File

@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System.Diagnostics;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI2;
internal static partial class Helper20240728
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.ConvertExcelToJson;
namespace File_Folder_Helper.ADO2024.PI3.ConvertExcelToJson;
public class FIBacklogMesa
{

View File

@ -1,10 +1,24 @@
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240805
{
private static void RenameFiles(string find, string replace, string[] ignoreFileNames, string[] confFiles)
{
string checkFile;
foreach (string confFile in confFiles)
{
if (ignoreFileNames.Contains(confFile))
continue;
checkFile = confFile.Replace(find, replace);
if (File.Exists(checkFile))
continue;
File.Move(confFile, checkFile);
}
}
private static void RenameFiles(ILogger<Worker> logger, string sourceDirectory, string[] files, string[] lines, string globalSettingsFile)
{
string to;
@ -53,20 +67,6 @@ internal static partial class Helper20240805
File.WriteAllText(globalSettingsFile, globalSettingsLines);
}
private static void RenameFiles(string find, string replace, string[] ignoreFileNames, string[] confFiles)
{
string checkFile;
foreach (string confFile in confFiles)
{
if (ignoreFileNames.Contains(confFile))
continue;
checkFile = confFile.Replace(find, replace);
if (File.Exists(checkFile))
continue;
File.Move(confFile, checkFile);
}
}
internal static void RenameFiles(ILogger<Worker> logger, List<string> args)
{
string find = args[6];

View File

@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System.Globalization;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240806
{

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240820
{

View File

@ -3,7 +3,7 @@ using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240822
{

View File

@ -5,7 +5,7 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240828
{
@ -68,33 +68,6 @@ internal static partial class Helper20240828
return result;
}
private static Record GetRecord(string[] lines, int i)
{
Record result;
int ii = i;
string line;
string[] segments;
string? lastDate = null;
List<ReadOnlyCollection<string>> matches = [];
for (int j = i; j >= 0; j--)
{
ii = j;
line = lines[j];
segments = line.Split(',');
if (segments.Length < 2)
continue;
lastDate ??= segments[0];
if (segments[0] != lastDate)
{
lastDate = segments[0];
break;
}
matches.Add(new(segments));
}
result = GetRecord(ii + 1, lastDate, new(matches));
return result;
}
private static ReadOnlyCollection<Record> GetRecords(string logDirectory, string logSearchPattern)
{
List<Record> results = [];
@ -130,6 +103,33 @@ internal static partial class Helper20240828
return new(results);
}
private static Record GetRecord(string[] lines, int i)
{
Record result;
int ii = i;
string line;
string[] segments;
string? lastDate = null;
List<ReadOnlyCollection<string>> matches = [];
for (int j = i; j >= 0; j--)
{
ii = j;
line = lines[j];
segments = line.Split(',');
if (segments.Length < 2)
continue;
lastDate ??= segments[0];
if (segments[0] != lastDate)
{
lastDate = segments[0];
break;
}
matches.Add(new(segments));
}
result = GetRecord(ii + 1, lastDate, new(matches));
return result;
}
private static Dictionary<int, ReadOnlyCollection<Record>> GetKeyValuePairs(string logSearchPattern, string logDirectory)
{
Dictionary<int, ReadOnlyCollection<Record>> results;

View File

@ -1,5 +1,5 @@
#if WorkItems
using File_Folder_Helper.Day.Q32024.WorkItems;
using File_Folder_Helper.ADO2024.PI3.WorkItems;
using Microsoft.Extensions.Logging;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.VisualStudio.Services.Common;
@ -10,7 +10,7 @@ using System.Text;
using System.Text.Json;
using System.Web;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240830
{
@ -42,6 +42,88 @@ internal static partial class Helper20240830
string WorkItemType,
float? WeightedShortestJobFirst);
private static void CompareWorkItems(ILogger<Worker> logger, string sourceDirectory, string api, string site, string query, string project, string basePage, string baseAddress, byte[] bytes, MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeaderValue, WorkItemTrackingHttpClient workItemTrackingHttpClient, HttpClient httpClient)
{
string base64 = Convert.ToBase64String(bytes);
httpClient.DefaultRequestHeaders.Authorization = new("Basic", base64);
httpClient.DefaultRequestHeaders.Accept.Add(mediaTypeWithQualityHeaderValue);
logger.LogInformation("{baseAddress}{basePage}/{project}{api}{query}", baseAddress, basePage, HttpUtility.HtmlEncode(project), api, query);
CompareWorkItems(httpClient, sourceDirectory, basePage, api, query, workItemTrackingHttpClient, project, site);
}
private static void GetSingle(HttpClient httpClient, string basePage, string api, string targetFileLocation, int id)
{
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, $"/workitems/{id}?%24expand=1"));
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
streamTask.Wait();
if (!streamTask.Result.CanRead)
throw new NullReferenceException(nameof(streamTask));
JsonElement? result = JsonSerializer.Deserialize<JsonElement>(streamTask.Result);
string file = Path.Combine(targetFileLocation, $"{-9}-{id}.json");
File.WriteAllText(file, result.ToString());
}
internal static void CompareWorkItems(ILogger<Worker> logger, List<string> args)
{
string api = args[6];
string pat = args[8];
string site = args[2];
string query = args[7];
string project = args[5];
string basePage = args[4];
string baseAddress = args[3];
string sourceDirectory = args[0];
VssBasicCredential credential = new("", pat);
byte[] bytes = Encoding.ASCII.GetBytes($":{pat}");
VssConnection connection = new(new(string.Concat(baseAddress, basePage)), credential);
MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeaderValue = new("application/json");
WorkItemTrackingHttpClient workItemTrackingHttpClient = connection.GetClient<WorkItemTrackingHttpClient>();
HttpClient httpClient = new(new HttpClientHandler() { UseDefaultCredentials = true }) { BaseAddress = new(baseAddress) };
CompareWorkItems(logger, sourceDirectory, api, site, query, project, basePage, baseAddress, bytes, mediaTypeWithQualityHeaderValue, workItemTrackingHttpClient, httpClient);
}
private static ReadOnlyCollection<WorkItem> GetWorkItems(ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
{
List<WorkItem> results = [];
Fields fields;
WorkItem workItem;
foreach (ValueWithReq valueWithReq in valueWithReqCollection)
{
fields = valueWithReq.Value.Fields;
workItem = new(fields.SystemAreaPath,
fields.SystemAssignedTo?.DisplayName,
fields.MicrosoftVSTSCommonBusinessValue == 0 ? null : fields.MicrosoftVSTSCommonBusinessValue,
fields.SystemChangedDate,
fields.MicrosoftVSTSCommonClosedDate == DateTime.MinValue ? null : fields.MicrosoftVSTSCommonClosedDate,
fields.SystemCommentCount,
fields.SystemCreatedDate,
fields.SystemDescription,
fields.MicrosoftVSTSSchedulingEffort == 0 ? null : fields.MicrosoftVSTSSchedulingEffort,
valueWithReq.Value.Id,
fields.SystemIterationPath,
fields.SystemParent == 0 ? null : fields.SystemParent,
fields.MicrosoftVSTSCommonPriority == 0 ? null : fields.MicrosoftVSTSCommonPriority,
valueWithReq.Value.Relations,
fields.CustomRequester?.DisplayName,
fields.MicrosoftVSTSCommonResolvedDate == DateTime.MinValue ? null : fields.MicrosoftVSTSCommonResolvedDate,
valueWithReq.Value.Rev,
fields.CustomRRminusOE == 0 ? null : fields.CustomRRminusOE,
fields.MicrosoftVSTSSchedulingStartDate == DateTime.MinValue ? null : fields.MicrosoftVSTSSchedulingStartDate,
fields.SystemState,
fields.SystemTags,
fields.MicrosoftVSTSSchedulingTargetDate == DateTime.MinValue ? null : fields.MicrosoftVSTSSchedulingTargetDate,
fields.MicrosoftVSTSCommonTimeCriticality == 0 ? null : fields.MicrosoftVSTSCommonTimeCriticality,
fields.SystemTitle,
fields.SystemWorkItemType,
fields.CustomWSJF == 0 ? null : fields.CustomWSJF);
results.Add(workItem);
}
return new(results);
}
private static string GetIds(HttpClient httpClient, string basePage, string api, string query)
{
List<int> results = [];
@ -71,21 +153,6 @@ internal static partial class Helper20240830
return result.ToString();
}
private static void GetSingle(HttpClient httpClient, string basePage, string api, string targetFileLocation, int id)
{
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, $"/workitems/{id}?%24expand=1"));
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
streamTask.Wait();
if (!streamTask.Result.CanRead)
throw new NullReferenceException(nameof(streamTask));
JsonElement? result = JsonSerializer.Deserialize<JsonElement>(streamTask.Result);
string file = Path.Combine(targetFileLocation, $"{-9}-{id}.json");
File.WriteAllText(file, result.ToString());
}
private static ReadOnlyCollection<ValueWithReq> GetWorkItems(HttpClient httpClient, string basePage, string api, string targetFileLocation, string ids)
{
List<ValueWithReq> results = [];
@ -126,45 +193,6 @@ internal static partial class Helper20240830
return new(results);
}
private static ReadOnlyCollection<WorkItem> GetWorkItems(ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
{
List<WorkItem> results = [];
Fields fields;
WorkItem workItem;
foreach (ValueWithReq valueWithReq in valueWithReqCollection)
{
fields = valueWithReq.Value.Fields;
workItem = new(fields.SystemAreaPath,
fields.SystemAssignedTo?.DisplayName,
fields.MicrosoftVSTSCommonBusinessValue == 0 ? null : fields.MicrosoftVSTSCommonBusinessValue,
fields.SystemChangedDate,
fields.MicrosoftVSTSCommonClosedDate == DateTime.MinValue ? null : fields.MicrosoftVSTSCommonClosedDate,
fields.SystemCommentCount,
fields.SystemCreatedDate,
fields.SystemDescription,
fields.MicrosoftVSTSSchedulingEffort == 0 ? null : fields.MicrosoftVSTSSchedulingEffort,
valueWithReq.Value.Id,
fields.SystemIterationPath,
fields.SystemParent == 0 ? null : fields.SystemParent,
fields.MicrosoftVSTSCommonPriority == 0 ? null : fields.MicrosoftVSTSCommonPriority,
valueWithReq.Value.Relations,
fields.CustomRequester?.DisplayName,
fields.MicrosoftVSTSCommonResolvedDate == DateTime.MinValue ? null : fields.MicrosoftVSTSCommonResolvedDate,
valueWithReq.Value.Rev,
fields.CustomRRminusOE == 0 ? null : fields.CustomRRminusOE,
fields.MicrosoftVSTSSchedulingStartDate == DateTime.MinValue ? null : fields.MicrosoftVSTSSchedulingStartDate,
fields.SystemState,
fields.SystemTags,
fields.MicrosoftVSTSSchedulingTargetDate == DateTime.MinValue ? null : fields.MicrosoftVSTSSchedulingTargetDate,
fields.MicrosoftVSTSCommonTimeCriticality == 0 ? null : fields.MicrosoftVSTSCommonTimeCriticality,
fields.SystemTitle,
fields.SystemWorkItemType,
fields.CustomWSJF == 0 ? null : fields.CustomWSJF);
results.Add(workItem);
}
return new(results);
}
private static void CompareWorkItems(WorkItemTrackingHttpClient workItemTrackingHttpClient,
string sourceDirectory,
string project,
@ -204,33 +232,5 @@ internal static partial class Helper20240830
CompareWorkItems(workItemTrackingHttpClient, targetFileLocation, project, site, valueWithReqCollection);
}
private static void CompareWorkItems(ILogger<Worker> logger, string sourceDirectory, string api, string site, string query, string project, string basePage, string baseAddress, byte[] bytes, MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeaderValue, WorkItemTrackingHttpClient workItemTrackingHttpClient, HttpClient httpClient)
{
string base64 = Convert.ToBase64String(bytes);
httpClient.DefaultRequestHeaders.Authorization = new("Basic", base64);
httpClient.DefaultRequestHeaders.Accept.Add(mediaTypeWithQualityHeaderValue);
logger.LogInformation("{baseAddress}{basePage}/{project}{api}{query}", baseAddress, basePage, HttpUtility.HtmlEncode(project), api, query);
CompareWorkItems(httpClient, sourceDirectory, basePage, api, query, workItemTrackingHttpClient, project, site);
}
internal static void CompareWorkItems(ILogger<Worker> logger, List<string> args)
{
string api = args[6];
string pat = args[8];
string site = args[2];
string query = args[7];
string project = args[5];
string basePage = args[4];
string baseAddress = args[3];
string sourceDirectory = args[0];
VssBasicCredential credential = new("", pat);
byte[] bytes = Encoding.ASCII.GetBytes($":{pat}");
VssConnection connection = new(new(string.Concat(baseAddress, basePage)), credential);
MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeaderValue = new("application/json");
WorkItemTrackingHttpClient workItemTrackingHttpClient = connection.GetClient<WorkItemTrackingHttpClient>();
HttpClient httpClient = new(new HttpClientHandler() { UseDefaultCredentials = true }) { BaseAddress = new(baseAddress) };
CompareWorkItems(logger, sourceDirectory, api, site, query, project, basePage, baseAddress, bytes, mediaTypeWithQualityHeaderValue, workItemTrackingHttpClient, httpClient);
}
}
#endif

View File

@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Globalization;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240910
{

View File

@ -0,0 +1,698 @@
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3;
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 Attributes);
public record Record(WorkItem WorkItem, WorkItem? Parent, ReadOnlyCollection<Record> Children);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Record[]))]
internal partial class RecordCollectionCommonSourceGenerationContext : JsonSerializerContext
{
}
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? Violation,
float? WeightedShortestJobFirst,
string WorkItemType)
{
public override string ToString() => $"{Id} - {WorkItemType} - {Title}";
public static WorkItem Get(WorkItem workItem, string? violation) =>
new(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,
workItem.Relations,
workItem.Requester,
workItem.ResolvedDate,
workItem.Revision,
workItem.RiskReductionMinusOpportunityEnablement,
workItem.StartDate,
workItem.State,
workItem.Tags,
workItem.TargetDate,
workItem.TimeCriticality,
workItem.Title,
workItem.Violation is null ? violation : workItem.Violation,
workItem.WeightedShortestJobFirst,
workItem.WorkItemType);
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WorkItem[]))]
internal partial class WorkItemSourceGenerationContext : JsonSerializerContext
{
}
private static int? GetIdFromUrlIfChild(Relation relation)
{
int? result;
string[] segments = relation?.Attributes is null || relation.Attributes.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 ReadOnlyCollection<Record> GetKeyValuePairs(ReadOnlyDictionary<int, WorkItem> keyValuePairs, WorkItem workItem, List<bool> nests)
{
List<Record> results = [];
int? childId;
nests.Add(true);
WorkItem? childWorkItem;
WorkItem? parentWorkItem;
List<WorkItem> collection = [];
ReadOnlyCollection<Record> records;
if (workItem.Relations is not null && workItem.Relations.Length > 0)
{
collection.Clear();
foreach (Relation relation in workItem.Relations)
{
childId = GetIdFromUrlIfChild(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 item in collection)
{
if (nests.Count > 99)
break;
if (item.Parent is null)
parentWorkItem = null;
else
_ = keyValuePairs.TryGetValue(item.Parent.Value, out parentWorkItem);
records = GetKeyValuePairs(keyValuePairs, item, nests);
results.Add(new(item, parentWorkItem, records));
}
}
return new(results);
}
private static void AppendLines(List<char> spaces, List<string> lines, Record record, bool condensed, bool sprintOnly)
{
string line;
spaces.Add('\t');
WorkItem workItem;
foreach (Record child in record.Children)
{
workItem = child.WorkItem;
line = GetLine(spaces, workItem, child, condensed, sprintOnly).TrimEnd();
lines.Add(line);
AppendLines(spaces, lines, child, condensed, sprintOnly);
}
spaces.RemoveAt(0);
}
private static void AppendLines(string url, List<char> spaces, List<string> lines, ReadOnlyCollection<Record> records, string workItemType)
{
List<string> results = [];
string? maxIterationPath;
List<string> distinct = [];
foreach (Record record in records)
{
// if (record.WorkItem.Id != 109724)
// continue;
if (record.WorkItem.WorkItemType != workItemType)
continue;
results.Add($"## {record.WorkItem.AssignedTo} - {record.WorkItem.Id} - {record.WorkItem.Title}");
results.Add(string.Empty);
results.Add($"- [{record.WorkItem.Id}]({url}{record.WorkItem.Id})");
if (record.Children.Count == 0)
results.Add(string.Empty);
else
{
AppendLines(spaces, results, record, condensed: true, sprintOnly: false);
results.Add(string.Empty);
distinct.Clear();
AppendLines(spaces, distinct, record, condensed: false, sprintOnly: true);
if (distinct.Count > 1)
{
results.Add($"## Distinct Iteration Path(s) - {record.WorkItem.WorkItemType} - {record.WorkItem.AssignedTo} - {record.WorkItem.Id} - {record.WorkItem.Title} - {record.WorkItem.IterationPath}");
results.Add(string.Empty);
results.Add($"- [{record.WorkItem.Id}]({url}{record.WorkItem.Id})");
distinct.Sort();
distinct = (from l in distinct select l.Trim()).Distinct().ToList();
results.AddRange(distinct);
results.Add(string.Empty);
maxIterationPath = distinct.Max();
if (!string.IsNullOrEmpty(maxIterationPath) && maxIterationPath.Contains("] ") && maxIterationPath.Split(']')[1].Trim() != record.WorkItem.IterationPath)
{
results.Add($"### Sync to Distinct Max Iteration Path => {maxIterationPath} - {record.WorkItem.Id} - {record.WorkItem.Title}");
results.Add(string.Empty);
}
}
results.Add($"## Extended - {record.WorkItem.Id} - {record.WorkItem.Title}");
results.Add(string.Empty);
AppendLines(spaces, results, record, condensed: false, sprintOnly: false);
results.Add(string.Empty);
}
lines.AddRange(results);
results.Clear();
}
}
private static string GetClosed(WorkItem workItem) =>
workItem.State != "Closed" ? "[ ]" : "[x]";
private static string GetLine(List<char> spaces, WorkItem workItem, Record record, bool condensed, bool sprintOnly)
{
string result;
string closed = GetClosed(workItem);
result = sprintOnly ? $"\t- [ ] {workItem.IterationPath}" :
condensed ? $"{new string(spaces.Skip(1).ToArray())}- {closed} {record.WorkItem.Id} - {workItem.Title}" :
$"{new string(spaces.Skip(1).ToArray())}- {closed} {record.WorkItem.Id} - {workItem.Title} ~~~ {workItem.AssignedTo} - {workItem.IterationPath.Replace('\\', '-')} - {workItem.CreatedDate} --- {workItem.ClosedDate}";
return result;
}
private static ReadOnlyCollection<WorkItem> GetWorkItemsNotMatching(string tags, ReadOnlyCollection<WorkItem> workItems)
{
List<WorkItem> results = [];
string[] segments;
string[] parentTags = tags.Split(';');
foreach (WorkItem workItem in workItems)
{
segments = tags.Split(';');
if (segments.Length > 0 && parentTags.Any(l => segments.Contains(l)))
continue;
results.Add(workItem);
}
return new(results);
}
private static ReadOnlyCollection<WorkItem> GetWorkItemsNotMatching(int? priority, ReadOnlyCollection<WorkItem> workItems)
{
List<WorkItem> results = [];
foreach (WorkItem workItem in workItems)
{
if (workItem.Priority == priority)
continue;
results.Add(workItem);
}
return new(results);
}
private static int GetState(WorkItem workItem) =>
workItem.State switch
{
"New" => 1,
"Active" => 2,
"Resolved" => 3,
"Closed" => 4,
"Removed" => 5,
_ => 8
};
private static ReadOnlyCollection<WorkItem> GetWorkItemsNotMatching(Record record, ReadOnlyCollection<WorkItem> workItems)
{
List<WorkItem> results = [];
int check;
int state = GetState(record.WorkItem);
List<KeyValuePair<int, WorkItem>> collection = [];
foreach (WorkItem workItem in workItems)
{
check = GetState(workItem);
if (check == state)
continue;
collection.Add(new(check, workItem));
}
foreach (KeyValuePair<int, WorkItem> keyValuePair in collection.OrderByDescending(l => l.Key))
results.Add(keyValuePair.Value);
return new(results);
}
private static ReadOnlyCollection<string> GetChildrenDirectories(ReadOnlyDictionary<int, Record> keyValuePairs, List<bool> nests, string parentDirectory, ReadOnlyCollection<Record> children)
{
List<string> results = [];
nests.Add(true);
string directory;
Record? childRecord;
ReadOnlyCollection<string> childrenDirectories;
foreach (Record record in children)
{
// if (record.WorkItem.Id == 110730)
// continue;
// if (record.WorkItem.Id == 110732)
// continue;
directory = Path.Combine(parentDirectory, $"{record.WorkItem.WorkItemType[..1]}-{record.WorkItem.Id}-{record.WorkItem.Title.Trim()[..1]}");
results.Add(directory);
if (!keyValuePairs.TryGetValue(record.WorkItem.Id, out childRecord))
continue;
if (nests.Count > 99)
break;
childrenDirectories = GetChildrenDirectories(keyValuePairs, nests, directory, childRecord.Children);
results.AddRange(childrenDirectories);
}
return new(results);
}
private static ReadOnlyDictionary<int, Record> GetKeyValuePairs(ReadOnlyDictionary<int, WorkItem> keyValuePairs)
{
Dictionary<int, Record> results = [];
List<bool> nests = [];
WorkItem? parentWorkItem;
ReadOnlyCollection<Record> records;
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
{
records = GetKeyValuePairs(keyValuePairs, keyValuePair.Value, nests);
results.Add(keyValuePair.Key, new(keyValuePair.Value, parentWorkItem, records));
}
catch (Exception)
{
results.Add(keyValuePair.Key, new(keyValuePair.Value, parentWorkItem, new([])));
}
}
return new(results);
}
private static ReadOnlyCollection<string> GetDirectories(string destinationDirectory, ReadOnlyDictionary<int, Record> keyValuePairs)
{
List<string> results = [];
Record record;
string directory;
List<bool> nests = [];
ReadOnlyCollection<string> childrenDirectories;
string ticksDirectory = Path.Combine(destinationDirectory, "_", DateTime.Now.Ticks.ToString());
foreach (KeyValuePair<int, Record> keyValuePair in keyValuePairs)
{
record = keyValuePair.Value;
if (record.Parent is not null && (record.WorkItem.Parent is null || record.Parent.Id != record.WorkItem.Parent.Value))
continue;
if (record.Parent is not null)
continue;
// if (record.WorkItem.Id == 110730)
// continue;
// if (record.WorkItem.Id == 110732)
// continue;
nests.Clear();
directory = Path.Combine(ticksDirectory, $"{record.WorkItem.WorkItemType[..1]}-{record.WorkItem.Id}-{record.WorkItem.Title.Trim()[..1]}");
childrenDirectories = GetChildrenDirectories(keyValuePairs, nests, directory, record.Children);
results.AddRange(childrenDirectories);
}
return new(results.Distinct().ToArray());
}
private static ReadOnlyCollection<WorkItem> FilterChildren(Record record, ReadOnlyCollection<string> workItemTypes)
{
List<WorkItem> results = [];
WorkItem workItem;
foreach (Record child in record.Children)
{
workItem = child.WorkItem;
if (!workItemTypes.Contains(workItem.WorkItemType))
continue;
results.Add(workItem);
}
return new(results);
}
private static string? GetMaxIterationPath(ReadOnlyCollection<WorkItem> workItems)
{
string? result;
List<string> results = [];
foreach (WorkItem workItem in workItems)
{
if (results.Contains(workItem.IterationPath))
continue;
results.Add(workItem.IterationPath);
}
result = results.Count == 0 ? null : results.Max();
return result;
}
private static ReadOnlyDictionary<int, Record> GetWorkItems(ILogger<Worker> logger, string developmentURL, string productionURL)
{
ReadOnlyDictionary<int, Record> results;
Dictionary<int, WorkItem> keyValuePairs = [];
Task<HttpResponseMessage> httpResponseMessage;
HttpClient httpClient = new(new HttpClientHandler { UseCookies = false });
httpResponseMessage = httpClient.GetAsync(developmentURL);
httpResponseMessage.Wait();
if (!httpResponseMessage.Result.IsSuccessStatusCode)
logger.LogWarning("{StatusCode} for {url}", httpResponseMessage.Result.StatusCode, developmentURL);
Task<string> developmentJSON = httpResponseMessage.Result.Content.ReadAsStringAsync();
developmentJSON.Wait();
httpResponseMessage = httpClient.GetAsync(productionURL);
httpResponseMessage.Wait();
if (!httpResponseMessage.Result.IsSuccessStatusCode)
logger.LogWarning("{StatusCode} for {url}", httpResponseMessage.Result.StatusCode, productionURL);
Task<string> productionJSON = httpResponseMessage.Result.Content.ReadAsStringAsync();
productionJSON.Wait();
if (developmentJSON.Result != developmentJSON.Result)
logger.LogWarning("developmentJSON doesn't match developmentJSON");
WorkItem[]? workItems = JsonSerializer.Deserialize(productionJSON.Result, WorkItemSourceGenerationContext.Default.WorkItemArray);
if (workItems is null)
logger.LogWarning("workItems is null");
else
{
foreach (WorkItem workItem in workItems)
keyValuePairs.Add(workItem.Id, workItem);
}
results = GetKeyValuePairs(new(keyValuePairs));
return results;
}
private static void WriteFileStructure(string destinationDirectory, ReadOnlyDictionary<int, Record> keyValuePairs)
{
ReadOnlyCollection<string> collection = GetDirectories(destinationDirectory, keyValuePairs);
foreach (string directory in collection)
{
if (directory.Length > 222)
continue;
if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory);
}
}
private static void WriteFiles(string destinationDirectory, ReadOnlyCollection<Record> records, string fileName)
{
string json = JsonSerializer.Serialize(records.ToArray(), RecordCollectionCommonSourceGenerationContext.Default.RecordArray);
string jsonFile = Path.Combine(destinationDirectory, $"{fileName}.json");
string jsonOld = !File.Exists(jsonFile) ? string.Empty : File.ReadAllText(jsonFile);
if (json != jsonOld)
File.WriteAllText(jsonFile, json);
}
private static void WriteFiles(string destinationDirectory, ReadOnlyCollection<string> lines, ReadOnlyCollection<WorkItem> workItems, string fileName)
{
string text = string.Join(Environment.NewLine, lines);
string markdownFile = Path.Combine(destinationDirectory, $"{fileName}.md");
string textOld = !File.Exists(markdownFile) ? string.Empty : File.ReadAllText(markdownFile);
if (text != textOld)
File.WriteAllText(markdownFile, text);
string html = CommonMark.CommonMarkConverter.Convert(text).Replace("<a href", "<a target='_blank' href");
string htmlFile = Path.Combine(destinationDirectory, $"{fileName}.html");
string htmlOld = !File.Exists(htmlFile) ? string.Empty : File.ReadAllText(htmlFile);
if (html != htmlOld)
File.WriteAllText(htmlFile, html);
string json = JsonSerializer.Serialize(workItems.ToArray(), WorkItemSourceGenerationContext.Default.WorkItemArray);
string jsonFile = Path.Combine(destinationDirectory, $"{fileName}.json");
string jsonOld = !File.Exists(jsonFile) ? string.Empty : File.ReadAllText(jsonFile);
if (json != jsonOld)
File.WriteAllText(jsonFile, json);
}
private static ReadOnlyCollection<WorkItem> FeatureCheckIterationPath122508(string url, List<string> lines, ReadOnlyCollection<string> workItemTypes, ReadOnlyCollection<Record> records, string workItemType)
{
List<WorkItem> results = [];
string? maxIterationPath;
List<string> collection = [];
List<string> violations = [];
ReadOnlyCollection<WorkItem> childrenWorkItems;
foreach (Record record in records)
{
if (record.WorkItem.WorkItemType != workItemType)
continue;
collection.Clear();
violations.Clear();
if (record.Children.Count == 0)
continue;
childrenWorkItems = FilterChildren(record, workItemTypes);
maxIterationPath = GetMaxIterationPath(childrenWorkItems);
if (string.IsNullOrEmpty(maxIterationPath) || record.WorkItem.IterationPath == maxIterationPath)
continue;
collection.Add($"## {record.WorkItem.AssignedTo} - {record.WorkItem.Id} - {record.WorkItem.Title}");
collection.Add(string.Empty);
collection.Add($"- [{record.WorkItem.Id}]({url}{record.WorkItem.Id})");
collection.Add($"- [ ] {record.WorkItem.Id} => {record.WorkItem.IterationPath} != {maxIterationPath}");
collection.Add(string.Empty);
lines.AddRange(collection);
results.Add(WorkItem.Get(record.WorkItem, $"IterationPath:<a target='_blank' href='{url}{record.WorkItem.Id}'>{record.WorkItem.Id}</a>;{record.WorkItem.IterationPath} != {maxIterationPath}"));
}
return new(results);
}
private static ReadOnlyCollection<WorkItem> FeatureCheckTag122514(string url, List<string> lines, ReadOnlyCollection<string> workItemTypes, ReadOnlyCollection<Record> records, string workItemType)
{
List<WorkItem> results = [];
List<string> collection = [];
List<string> violations = [];
ReadOnlyCollection<WorkItem> childrenWorkItems;
ReadOnlyCollection<WorkItem> workItemsNotMatching;
foreach (Record record in records)
{
if (record.WorkItem.WorkItemType != workItemType)
continue;
collection.Clear();
violations.Clear();
if (record.Children.Count == 0)
continue;
if (string.IsNullOrEmpty(record.WorkItem.Tags))
workItemsNotMatching = new([record.WorkItem]);
else
{
childrenWorkItems = FilterChildren(record, workItemTypes);
workItemsNotMatching = GetWorkItemsNotMatching(record.WorkItem.Tags, childrenWorkItems);
if (!string.IsNullOrEmpty(record.WorkItem.Tags) && workItemsNotMatching.Count == 0)
continue;
}
collection.Add($"## {record.WorkItem.AssignedTo} - {record.WorkItem.Id} - {record.WorkItem.Title}");
collection.Add(string.Empty);
collection.Add($"- [{record.WorkItem.Id}]({url}{record.WorkItem.Id})");
foreach (WorkItem workItem in workItemsNotMatching)
collection.Add($"- [ ] {workItem} {nameof(record.WorkItem.Tags)} != {record.WorkItem.Tags}");
collection.Add(string.Empty);
lines.AddRange(collection);
violations.Add($"Tag:{record.WorkItem.Tags};");
foreach (WorkItem workItem in workItemsNotMatching)
violations.Add($"<a target='_blank' href='{url}{workItem.Id}'>{workItem.Id}</a>:{workItem.Tags};");
results.Add(WorkItem.Get(record.WorkItem, string.Join(' ', violations)));
}
return new(results);
}
private static ReadOnlyCollection<WorkItem> FeatureCheckPriority126169(string url, List<string> lines, ReadOnlyCollection<string> workItemTypes, ReadOnlyCollection<Record> records, string workItemType)
{
List<WorkItem> results = [];
List<string> collection = [];
List<string> violations = [];
ReadOnlyCollection<WorkItem> childrenWorkItems;
ReadOnlyCollection<WorkItem> workItemsNotMatching;
foreach (Record record in records)
{
if (record.WorkItem.WorkItemType != workItemType)
continue;
collection.Clear();
violations.Clear();
if (record.Children.Count == 0)
continue;
childrenWorkItems = FilterChildren(record, workItemTypes);
workItemsNotMatching = GetWorkItemsNotMatching(record.WorkItem.Priority, childrenWorkItems);
if (workItemsNotMatching.Count == 0)
continue;
collection.Add($"## {record.WorkItem.AssignedTo} - {record.WorkItem.Id} - {record.WorkItem.Title}");
collection.Add(string.Empty);
collection.Add($"- [{record.WorkItem.Id}]({url}{record.WorkItem.Id})");
foreach (WorkItem workItem in workItemsNotMatching)
collection.Add($"- [ ] [{workItem.Id}]({url}{workItem.Id}) {nameof(record.WorkItem.Priority)} != {record.WorkItem.Priority}");
collection.Add(string.Empty);
lines.AddRange(collection);
violations.Add($"Priority:{record.WorkItem.Priority};");
foreach (WorkItem workItem in workItemsNotMatching)
violations.Add($"<a target='_blank' href='{url}{workItem.Id}'>{workItem.Id}</a>:{workItem.Priority};");
results.Add(WorkItem.Get(record.WorkItem, string.Join(' ', violations)));
}
return new(results);
}
private static ReadOnlyCollection<WorkItem> FeatureCheckState123066(string url, List<string> lines, ReadOnlyCollection<string> workItemTypes, ReadOnlyCollection<Record> records, string workItemType)
{
List<WorkItem> results = [];
List<string> collection = [];
List<string> violations = [];
ReadOnlyCollection<WorkItem> childrenWorkItems;
ReadOnlyCollection<WorkItem> workItemsNotMatching;
foreach (Record record in records)
{
if (record.WorkItem.WorkItemType != workItemType)
continue;
collection.Clear();
violations.Clear();
if (record.WorkItem.State == "New")
continue;
if (record.Children.Count == 0)
continue;
childrenWorkItems = FilterChildren(record, workItemTypes);
workItemsNotMatching = GetWorkItemsNotMatching(record, childrenWorkItems);
if (workItemsNotMatching.Count == 0)
continue;
collection.Add($"## {record.WorkItem.AssignedTo} - {record.WorkItem.Id} - {record.WorkItem.Title}");
collection.Add(string.Empty);
collection.Add($"- [{record.WorkItem.Id}]({url}{record.WorkItem.Id})");
foreach (WorkItem workItem in workItemsNotMatching)
collection.Add($"- [ ] [{workItem.Id}]({url}{workItem.Id}) {nameof(record.WorkItem.State)} != {record.WorkItem.State}");
collection.Add(string.Empty);
lines.AddRange(collection);
violations.Add($"State:{record.WorkItem.State};");
foreach (WorkItem workItem in workItemsNotMatching)
violations.Add($"<a target='_blank' href='{url}{workItem.Id}'>{workItem.Id}</a>:{workItem.State};");
results.Add(WorkItem.Get(record.WorkItem, string.Join(' ', violations)));
}
return new(results);
}
private static ReadOnlyCollection<WorkItem> FeatureCheckState123067(string url, List<string> lines, ReadOnlyCollection<string> workItemTypes, ReadOnlyCollection<Record> records, string workItemType)
{
List<WorkItem> results = [];
List<string> collection = [];
List<string> violations = [];
ReadOnlyCollection<WorkItem> childrenWorkItems;
ReadOnlyCollection<WorkItem> workItemsNotMatching;
foreach (Record record in records)
{
if (record.WorkItem.WorkItemType != workItemType)
continue;
collection.Clear();
violations.Clear();
if (record.Children.Count == 0)
continue;
childrenWorkItems = FilterChildren(record, workItemTypes);
workItemsNotMatching = GetWorkItemsNotMatching(record, childrenWorkItems);
if (workItemsNotMatching.Count == 0)
continue;
collection.Add($"## {record.WorkItem.AssignedTo} - {record.WorkItem.Id} - {record.WorkItem.Title}");
collection.Add(string.Empty);
collection.Add($"- [{record.WorkItem.Id}]({url}{record.WorkItem.Id})");
foreach (WorkItem workItem in workItemsNotMatching)
collection.Add($"- [ ] [{workItem.Id}]({url}{workItem.Id}) {nameof(record.WorkItem.State)} != {record.WorkItem.State}");
collection.Add(string.Empty);
lines.AddRange(collection);
violations.Add($"State:{record.WorkItem.State};");
foreach (WorkItem workItem in workItemsNotMatching)
violations.Add($"<a target='_blank' href='{url}{workItem.Id}'>{workItem.Id}</a>:{workItem.State};");
results.Add(WorkItem.Get(record.WorkItem, string.Join(' ', violations)));
}
return new(results);
}
internal static void WriteMarkdown(ILogger<Worker> logger, List<string> args)
{
string url = args[5];
List<char> spaces = [];
List<string> lines = [];
ReadOnlyCollection<WorkItem> results;
string[] workItemTypes = args[4].Split('|');
string sourceDirectory = Path.GetFullPath(args[0]);
string destinationDirectory = Path.GetFullPath(args[6]);
if (!Directory.Exists(destinationDirectory))
_ = Directory.CreateDirectory(destinationDirectory);
ReadOnlyDictionary<int, Record> keyValuePairs = GetWorkItems(logger, args[2], args[3]);
WriteFileStructure(destinationDirectory, keyValuePairs);
ReadOnlyCollection<Record> records = new(keyValuePairs.Values.ToArray());
logger.LogInformation("Found {workItems} workItemAndChildren", records.Count);
ReadOnlyCollection<string> bugUserStoryWorkItemTypes = new(new string[] { "Bug", "User Story" });
ReadOnlyCollection<string> bugUserStoryTaskWorkItemTypes = new(new string[] { "Bug", "User Story", "Task" });
WriteFiles(destinationDirectory, records, "with-parents");
foreach (string workItemType in workItemTypes)
{
lines.Clear();
lines.Add($"# {workItemType}");
lines.Add(string.Empty);
AppendLines(url, spaces, lines, records, workItemType);
results = new([]);
WriteFiles(destinationDirectory, new(lines), results, workItemType);
}
{
lines.Clear();
string workItemType = "Feature";
lines.Add($"# {nameof(FeatureCheckIterationPath122508)}");
lines.Add(string.Empty);
results = FeatureCheckIterationPath122508(url, lines, bugUserStoryTaskWorkItemTypes, records, workItemType);
WriteFiles(destinationDirectory, new(lines), results, "check-122508");
}
{
lines.Clear();
string workItemType = "Feature";
lines.Add($"# {nameof(FeatureCheckTag122514)}");
lines.Add(string.Empty);
results = FeatureCheckTag122514(url, lines, bugUserStoryWorkItemTypes, records, workItemType);
WriteFiles(destinationDirectory, new(lines), results, "check-122514");
}
{
lines.Clear();
string workItemType = "Feature";
lines.Add($"# {nameof(FeatureCheckPriority126169)}");
lines.Add(string.Empty);
results = FeatureCheckPriority126169(url, lines, bugUserStoryWorkItemTypes, records, workItemType);
WriteFiles(destinationDirectory, new(lines), results, "check-126169");
}
{
lines.Clear();
string workItemType = "Feature";
lines.Add($"# {nameof(FeatureCheckState123066)}");
lines.Add(string.Empty);
results = FeatureCheckState123066(url, lines, bugUserStoryTaskWorkItemTypes, records, workItemType);
WriteFiles(destinationDirectory, new(lines), results, "check-123066");
}
{
lines.Clear();
string workItemType = "Feature";
lines.Add($"# {nameof(FeatureCheckState123067)}");
lines.Add(string.Empty);
results = FeatureCheckState123067(url, lines, bugUserStoryTaskWorkItemTypes, records, workItemType);
WriteFiles(destinationDirectory, new(lines), results, "check-123067");
}
}
}

View File

@ -1,6 +1,6 @@
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240916
{

View File

@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240925
{

View File

@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q42024;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20241002
{
@ -68,6 +68,30 @@ internal static partial class Helper20241002
return result;
}
private static ReadOnlyDictionary<string, Record> GetKeyValuePairs(ReadOnlyDictionary<string, Record> keyValuePairs)
{
Dictionary<string, Record> results = [];
Record result;
Record record;
string? calculation;
foreach (KeyValuePair<string, Record> keyValuePair in keyValuePairs)
{
record = keyValuePair.Value;
calculation = record.RawCalculation;
if (calculation is not null)
{
foreach (KeyValuePair<string, Record> kVP in keyValuePairs)
calculation = calculation.Replace(kVP.Key, $"%DCS(Value, {kVP.Value.Test})");
}
result = Record.Get(record, calculation);
results.Add(keyValuePair.Key, result);
}
return new(results);
}
private static string GetKey(Record record) =>
$"ch({record.Id + 1})";
private static ReadOnlyCollection<Record> GetRecords(string sourceDirectory, string searchPattern, string searchPatternB, string searchPatternC)
{
List<Record> results = [];
@ -112,9 +136,6 @@ internal static partial class Helper20241002
return new(results);
}
private static string GetKey(Record record) =>
$"ch({record.Id + 1})";
private static ReadOnlyDictionary<string, Record> GetKeyValuePairs(ReadOnlyCollection<Record> records)
{
Dictionary<string, Record> results = [];
@ -133,27 +154,6 @@ internal static partial class Helper20241002
return new(results);
}
private static ReadOnlyDictionary<string, Record> GetKeyValuePairs(ReadOnlyDictionary<string, Record> keyValuePairs)
{
Dictionary<string, Record> results = [];
Record result;
Record record;
string? calculation;
foreach (KeyValuePair<string, Record> keyValuePair in keyValuePairs)
{
record = keyValuePair.Value;
calculation = record.RawCalculation;
if (calculation is not null)
{
foreach (KeyValuePair<string, Record> kVP in keyValuePairs)
calculation = calculation.Replace(kVP.Key, $"%DCS(Value, {kVP.Value.Test})");
}
result = Record.Get(record, calculation);
results.Add(keyValuePair.Key, result);
}
return new(results);
}
internal static void ConvertInfinityQSProjectFiles(ILogger<Worker> logger, List<string> args)
{
string searchPattern = args[2];

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WIQL;
namespace File_Folder_Helper.ADO2024.PI3.WIQL;
public class Column
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WIQL;
namespace File_Folder_Helper.ADO2024.PI3.WIQL;
public class Field
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WIQL;
namespace File_Folder_Helper.ADO2024.PI3.WIQL;
public class Root
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WIQL;
namespace File_Folder_Helper.ADO2024.PI3.WIQL;
public class SortColumn
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WIQL;
namespace File_Folder_Helper.ADO2024.PI3.WIQL;
public class WorkItem
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WorkItems;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class Avatar
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WorkItems;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class CommentVersionRef
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WorkItems;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class CustomRequester
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WorkItems;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class Fields
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WorkItems;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class Html
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WorkItems;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class Links
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WorkItems;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class SystemAssignedTo
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WorkItems;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class SystemChangedBy
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WorkItems;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class SystemCreatedBy
{

View File

@ -1,7 +1,7 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Day.Q32024.WorkItems;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class Value
{

View File

@ -1,5 +1,5 @@
#if WorkItems
namespace File_Folder_Helper.Day.Q32024.WorkItems;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class ValueWithReq
{

View File

@ -34,85 +34,85 @@ internal static class HelperDay
else if (args[1] == "Day-Helper-2023-12-22")
Day.Q42023.Helper20231222.ConvertId(logger, args);
else if (args[1] == "Day-Helper-2024-01-05")
Day.Q12024.Helper20240105.ConvertKeePassExport(logger, args);
ADO2024.PI1.Helper20240105.ConvertKeePassExport(logger, args);
else if (args[1] == "Day-Helper-2024-01-06")
Day.Q12024.Helper20240106.TextToJson(logger, args);
ADO2024.PI1.Helper20240106.TextToJson(logger, args);
else if (args[1] == "Day-Helper-2024-01-07")
Day.Q12024.Helper20240107.DirectoryToISO(logger, args);
ADO2024.PI1.Helper20240107.DirectoryToISO(logger, args);
else if (args[1] == "Day-Helper-2024-01-08")
Day.Q12024.Helper20240108.SortCodeMethods(logger, args, cancellationToken);
ADO2024.PI1.Helper20240108.SortCodeMethods(logger, args, cancellationToken);
else if (args[1] == "Day-Helper-2024-01-27")
logger.LogError("{arg} - has been migrated to Clipboard_Send_Keys", args[1]);
else if (args[1] == "Day-Helper-2024-01-29")
Day.Q12024.Helper20240129.JsonToTsv(logger, args);
ADO2024.PI1.Helper20240129.JsonToTsv(logger, args);
else if (args[1] == "Day-Helper-2024-03-05")
Day.Q12024.Helper20240305.ArchiveFiles(logger, args);
ADO2024.PI1.Helper20240305.ArchiveFiles(logger, args);
else if (args[1] == "Day-Helper-2024-04-03")
Day.Q22024.Helper20240403.AlertIfNewDeviceIsConnected(logger, args);
ADO2024.PI1.Helper20240403.AlertIfNewDeviceIsConnected(logger, args);
else if (args[1] == "Day-Helper-2024-04-04")
Day.Q22024.Helper20240404.ParseCSV(logger, args);
ADO2024.PI1.Helper20240404.ParseCSV(logger, args);
else if (args[1] == "Day-Helper-2024-04-09")
Day.Q22024.Helper20240409.MonA(logger, args);
ADO2024.PI1.Helper20240409.MonA(logger, args);
else if (args[1] == "Day-Helper-2024-04-17")
Day.Q22024.Helper20240417.FilteredRunCommand(logger, args, cancellationToken);
ADO2024.PI1.Helper20240417.FilteredRunCommand(logger, args, cancellationToken);
else if (args[1] == "Day-Helper-2024-04-26")
Day.Q22024.Helper20240426.UpdateTests(logger, args);
ADO2024.PI1.Helper20240426.UpdateTests(logger, args);
else if (args[1] == "Day-Helper-2024-04-27")
Day.Q22024.Helper20240427.Immich(appSettings, logger, args);
ADO2024.PI1.Helper20240427.Immich(appSettings, logger, args);
else if (args[1] == "Day-Helper-2024-04-29")
Day.Q22024.Helper20240429.GitConfigCleanUp(logger, args);
ADO2024.PI2.Helper20240429.GitConfigCleanUp(logger, args);
else if (args[1] == "Day-Helper-2024-05-10")
Day.Q22024.Helper20240510.PullIconsForBLM(logger, args);
ADO2024.PI2.Helper20240510.PullIconsForBLM(logger, args);
else if (args[1] == "Day-Helper-2024-05-13")
Day.Q22024.Helper20240513.PersonKeyToName(logger, args);
ADO2024.PI2.Helper20240513.PersonKeyToName(logger, args);
else if (args[1] == "Day-Helper-2024-05-17")
Day.Q22024.Helper20240517.SaveAmazon(logger, args);
ADO2024.PI2.Helper20240517.SaveAmazon(logger, args);
else if (args[1] == "Day-Helper-2024-05-18")
Day.Q22024.Helper20240518.PersonKeyToImmichImport(logger, args);
ADO2024.PI2.Helper20240518.PersonKeyToImmichImport(logger, args);
else if (args[1] == "Day-Helper-2024-05-19")
Day.Q22024.Helper20240519.FindReplaceDirectoryName(logger, args);
ADO2024.PI2.Helper20240519.FindReplaceDirectoryName(logger, args);
else if (args[1] == "Day-Helper-2024-05-20")
Day.Q22024.Helper20240520.IdentifierRename(logger, args);
ADO2024.PI2.Helper20240520.IdentifierRename(logger, args);
else if (args[1] == "Day-Helper-2024-06-23")
Day.Q22024.Helper20240623.UpdateSubTasksInMarkdownFiles(logger, args);
ADO2024.PI2.Helper20240623.UpdateSubTasksInMarkdownFiles(logger, args);
else if (args[1] == "Day-Helper-2024-06-24")
Day.Q22024.Helper20240624.MoveUpOneDirectory(logger, args);
ADO2024.PI2.Helper20240624.MoveUpOneDirectory(logger, args);
else if (args[1] == "Day-Helper-2024-07-11")
Day.Q32024.Helper20240711.GitRemoteRemove(logger, args);
ADO2024.PI2.Helper20240711.GitRemoteRemove(logger, args);
else if (args[1] == "Day-Helper-2024-07-18")
Day.Q32024.Helper20240718.JsonToMarkdown(logger, args);
ADO2024.PI2.Helper20240718.JsonToMarkdown(logger, args);
else if (args[1] == "Day-Helper-2024-07-24")
Day.Q32024.Helper20240724.CopyDirectories(logger, args);
ADO2024.PI2.Helper20240724.CopyDirectories(logger, args);
else if (args[1] == "Day-Helper-2024-07-28")
Day.Q32024.Helper20240728.DownloadSslCertificates(logger, args);
ADO2024.PI2.Helper20240728.DownloadSslCertificates(logger, args);
else if (args[1] == "Day-Helper-2024-08-05")
Day.Q32024.Helper20240805.RenameFiles(logger, args);
ADO2024.PI3.Helper20240805.RenameFiles(logger, args);
else if (args[1] == "Day-Helper-2024-08-06")
Day.Q32024.Helper20240806.ArchiveFiles(logger, args);
ADO2024.PI3.Helper20240806.ArchiveFiles(logger, args);
#if WorkItems
else if (args[1] == "Day-Helper-2024-08-09")
Day.Q32024.Helper20240809.CreateWorkItems(logger, args);
ADO2024.PI3.Helper20240809.CreateWorkItems(logger, args);
#endif
else if (args[1] == "Day-Helper-2024-08-20")
Day.Q32024.Helper20240820.MoveFilesWithSleep(logger, args);
ADO2024.PI3.Helper20240820.MoveFilesWithSleep(logger, args);
else if (args[1] == "Day-Helper-2024-08-22")
Day.Q32024.Helper20240822.ParseKanbn(logger, args);
ADO2024.PI3.Helper20240822.ParseKanbn(logger, args);
else if (args[1] == "Day-Helper-2024-08-28")
Day.Q32024.Helper20240828.MoveWaferCounterToArchive(logger, args);
ADO2024.PI3.Helper20240828.MoveWaferCounterToArchive(logger, args);
#if WorkItems
else if (args[1] == "Day-Helper-2024-08-30")
Day.Q32024.Helper20240830.CompareWorkItems(logger, args);
ADO2024.PI3.Helper20240830.CompareWorkItems(logger, args);
#endif
else if (args[1] == "Day-Helper-2024-09-10")
Day.Q32024.Helper20240910.MoveFilesToWeekOfYear(logger, args);
ADO2024.PI3.Helper20240910.MoveFilesToWeekOfYear(logger, args);
else if (args[1] == "Day-Helper-2024-09-11")
Day.Q32024.Helper20240911.WriteMarkdown(logger, args);
ADO2024.PI3.Helper20240911.WriteMarkdown(logger, args);
else if (args[1] == "Day-Helper-2024-09-16")
Day.Q32024.Helper20240916.DebugProxyPass(logger, args);
ADO2024.PI3.Helper20240916.DebugProxyPass(logger, args);
else if (args[1] == "Day-Helper-2024-09-25")
Day.Q32024.Helper20240925.DistinctTests(logger, args);
ADO2024.PI3.Helper20240925.DistinctTests(logger, args);
else if (args[1] == "Day-Helper-2024-10-02")
Day.Q42024.Helper20241002.ConvertInfinityQSProjectFiles(logger, args);
ADO2024.PI3.Helper20241002.ConvertInfinityQSProjectFiles(logger, args);
else
throw new Exception(appSettings.Company);
}

View File

@ -1,705 +0,0 @@
#if WorkItems
using File_Folder_Helper.Day.Q32024.ConvertExcelToJson;
using File_Folder_Helper.Day.Q32024.WorkItems;
using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.VisualStudio.Services.WebApi.Patch;
using Microsoft.VisualStudio.Services.WebApi.Patch.Json;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Web;
namespace File_Folder_Helper.Day.Q32024;
internal static partial class Helper20240809
{
private static void AddPatch(JsonPatchDocument document, string path, object value) =>
document.Add(new JsonPatchOperation { From = null, Operation = Operation.Add, Path = path, Value = value });
private static Dictionary<string, FIBacklogMesa> GetFIBacklogMesaCollection(string json)
{
Dictionary<string, FIBacklogMesa> results = [];
string key;
FIBacklogMesa[]? fiBacklogMesaCollection;
fiBacklogMesaCollection = JsonSerializer.Deserialize(json, FIBacklogMesaCollectionSourceGenerationContext.Default.FIBacklogMesaArray);
if (fiBacklogMesaCollection is null || fiBacklogMesaCollection.Length == 0)
throw new NullReferenceException();
foreach (FIBacklogMesa fiBacklogMesa in fiBacklogMesaCollection)
{
if (string.IsNullOrEmpty(fiBacklogMesa.Req))
continue;
if (string.IsNullOrEmpty(fiBacklogMesa.Submitted))
continue;
if (string.IsNullOrEmpty(fiBacklogMesa.Requestor))
continue;
key = $"{fiBacklogMesa.Req} - ";
if (results.ContainsKey(key))
continue;
results.Add(key, fiBacklogMesa);
}
return results;
}
private static string GetIds(HttpClient httpClient, string basePage, string api, string query)
{
StringBuilder result = new();
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, query));
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
streamTask.Wait();
if (!streamTask.Result.CanRead)
throw new NullReferenceException(nameof(streamTask));
WIQL.Root? root = JsonSerializer.Deserialize(streamTask.Result, WIQL.WIQLRootSourceGenerationContext.Default.Root);
streamTask.Result.Dispose();
if (root is null || root.WorkItems is null)
throw new NullReferenceException(nameof(root));
foreach (WIQL.WorkItem workItem in root.WorkItems)
_ = result.Append(workItem.Id).Append(',');
if (result.Length > 0)
_ = result.Remove(result.Length - 1, 1);
return result.ToString();
}
private static ReadOnlyCollection<ValueWithReq> GetWorkItems(HttpClient httpClient, string basePage, string api, string sourceDirectory, string ids)
{
List<ValueWithReq> results = [];
int req;
string json;
string file;
Value? value;
string[] segments;
JsonElement[] jsonElements;
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, $"/workitems?ids={ids}"));
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
streamTask.Wait();
if (!streamTask.Result.CanRead)
throw new NullReferenceException(nameof(streamTask));
JsonElement? result = JsonSerializer.Deserialize<JsonElement>(streamTask.Result);
if (result is null || result.Value.ValueKind != JsonValueKind.Object)
throw new NullReferenceException(nameof(result));
JsonProperty[] jsonProperties = result.Value.EnumerateObject().ToArray();
foreach (JsonProperty jsonProperty in jsonProperties)
{
if (jsonProperty.Value.ValueKind != JsonValueKind.Array)
continue;
jsonElements = jsonProperty.Value.EnumerateArray().ToArray();
foreach (JsonElement jsonElement in jsonElements)
{
json = jsonElement.GetRawText();
value = JsonSerializer.Deserialize(json, ValueSourceGenerationContext.Default.Value);
if (value is null)
continue;
segments = value.Fields.SystemTitle.Split('-');
if (segments.Length < 2)
continue;
if (!int.TryParse(segments[0], out req) || req == 0)
continue;
file = Path.Combine(sourceDirectory, $"{req}-{value.Id}.json");
File.WriteAllText(file, json);
results.Add(new(value, req, json));
}
}
return new(results);
}
private static ReadOnlyCollection<ValueWithReq> RemoveFrom(Dictionary<string, FIBacklogMesa> keyToFIBacklogMesa, ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
{
List<ValueWithReq> results = [];
foreach (ValueWithReq valueWithReq in valueWithReqCollection)
{
if (keyToFIBacklogMesa.Remove($"{valueWithReq.Req} - "))
continue;
results.Add(valueWithReq);
}
return new(results);
}
private static ReadOnlyCollection<Models.Comment> GetComments(HttpClient httpClient, string basePage, string api, string sourceDirectory, int req, int id)
{
List<Models.Comment> results = [];
string json;
string file;
Models.Comment? comment;
JsonElement[] jsonElements;
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, $"/workitems/{id}/comments?api-version=7.0-preview.3"));
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
streamTask.Wait();
if (!streamTask.Result.CanRead)
throw new NullReferenceException(nameof(streamTask));
JsonElement? result = JsonSerializer.Deserialize<JsonElement>(streamTask.Result);
if (result is null || result.Value.ValueKind != JsonValueKind.Object)
throw new NullReferenceException(nameof(result));
JsonProperty[] jsonProperties = result.Value.EnumerateObject().ToArray();
foreach (JsonProperty jsonProperty in jsonProperties)
{
if (jsonProperty.Value.ValueKind != JsonValueKind.Array)
continue;
jsonElements = jsonProperty.Value.EnumerateArray().ToArray();
foreach (JsonElement jsonElement in jsonElements)
{
json = jsonElement.GetRawText();
comment = JsonSerializer.Deserialize(jsonElement, CommentSourceGenerationContext.Default.Comment);
if (comment is null || comment.WorkItemId is null || comment.Id is null)
continue;
file = Path.Combine(sourceDirectory, $"{req}-{id}-{comment.Id}-comments.json");
File.WriteAllText(file, json);
results.Add(comment);
}
}
return new(results);
}
private static void UpdateComment(HttpClient httpClient,
string basePage,
string api,
int id,
FIBacklogMesa fiBacklogMesa,
Models.Comment comment)
{
DateTime submittedDateTime;
if (!DateTime.TryParse(fiBacklogMesa.Submitted, out submittedDateTime))
submittedDateTime = DateTime.MinValue;
string updatesWithSubmitted = $"{fiBacklogMesa.Updates}<br>&nbsp;<br>{submittedDateTime:MM/dd/yyyy} - Submitted by {fiBacklogMesa.Requestor}";
string json = JsonSerializer.Serialize(new { text = updatesWithSubmitted });
StringContent stringContent = new(json, Encoding.UTF8, "application/json");
string requestUri = string.Concat(basePage, api, $"/workitems/{id}/comments/{comment.Id}?api-version=7.0-preview.3");
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.PatchAsync(requestUri, stringContent);
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
streamTask.Wait();
if (!streamTask.Result.CanRead)
throw new NullReferenceException(nameof(streamTask));
JsonElement? result = JsonSerializer.Deserialize<JsonElement>(streamTask.Result);
if (result is null || result.Value.ValueKind != JsonValueKind.Object)
throw new NullReferenceException(nameof(result));
}
private static void UpdateAllWorkItemsPharesComment(HttpClient httpClient,
string basePage,
string api,
string sourceDirectory,
Dictionary<string, FIBacklogMesa> keyToFIBacklogMesa,
ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
{
string key;
FIBacklogMesa? fIBacklogMesa;
ReadOnlyCollection<Models.Comment> comments;
foreach (ValueWithReq valueWithReq in valueWithReqCollection)
{
if (valueWithReq.Value.Fields.SystemCommentCount == 0)
continue;
key = $"{valueWithReq.Req} - ";
if (!keyToFIBacklogMesa.TryGetValue(key, out fIBacklogMesa))
continue;
comments = GetComments(httpClient, basePage, api, sourceDirectory, valueWithReq.Req, valueWithReq.Value.Id);
foreach (Models.Comment comment in comments)
{
if (comment.CreatedBy?.UniqueName is null || !comment.CreatedBy.UniqueName.Contains("Phares", StringComparison.CurrentCultureIgnoreCase))
continue;
UpdateComment(httpClient, basePage, api, valueWithReq.Value.Id, fIBacklogMesa, comment);
}
}
}
private static void UpdateIteration(HttpClient httpClient,
string basePage,
string api,
int id,
int rev)
{
string json = /*lang=json,strict*/ string.Concat("[ { \"op\": \"test\", \"path\": \"/rev\", \"value\": ", rev, " }, { \"op\": \"replace\", \"path\": \"/fields/System.IterationPath\", \"value\": \"ART SPS\" } ]");
StringContent stringContent = new(json, Encoding.UTF8, "application/json-patch+json");
string requestUri = string.Concat(basePage, api, $"/workitems/{id}?api-version=1.0");
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.PatchAsync(requestUri, stringContent);
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<Stream> streamTask = httpResponseMessageTask.Result.Content.ReadAsStreamAsync();
streamTask.Wait();
if (!streamTask.Result.CanRead)
throw new NullReferenceException(nameof(streamTask));
JsonElement? result = JsonSerializer.Deserialize<JsonElement>(streamTask.Result);
if (result is null || result.Value.ValueKind != JsonValueKind.Object)
throw new NullReferenceException(nameof(result));
}
private static void UpdateAllFeaturesNotArtSPS(HttpClient httpClient,
string basePage,
string api,
ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
{
foreach (ValueWithReq valueWithReq in valueWithReqCollection)
{
if (valueWithReq.Value.Fields.SystemIterationPath != "ART SPS\\2024")
continue;
UpdateIteration(httpClient, basePage, api, valueWithReq.Value.Id, valueWithReq.Value.Rev);
}
}
private static DateTime? GetCommitDate(FIBacklogMesa fiBacklogMesa)
{
DateTime? result;
DateTime dateTime;
DateTime minDateTime = DateTime.MinValue.AddYears(10);
string commitDate = fiBacklogMesa.CommitDate.Split(' ')[0];
if (string.IsNullOrEmpty(commitDate))
result = null;
else
{
if (DateTime.TryParseExact(commitDate, "MM/dd/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime) && dateTime >= minDateTime)
result = dateTime.AddHours(12).ToUniversalTime();
else
{
if (DateTime.TryParseExact(commitDate, "dd-MMM-yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime) && dateTime >= minDateTime)
result = dateTime.AddHours(12).ToUniversalTime();
else
{
if (DateTime.TryParse(commitDate, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime) && dateTime >= minDateTime)
result = dateTime.AddHours(12).ToUniversalTime();
else
result = null;
}
}
}
return result;
}
private static int GetPriority(FIBacklogMesa fiBacklogMesa)
{
int result;
if (string.IsNullOrEmpty(fiBacklogMesa.Priority) || !int.TryParse(fiBacklogMesa.Priority[..1], out int priority) || priority == 0 || priority > 3)
result = 4;
else
result = priority;
return result;
}
private static int? GetPrioritySubset(FIBacklogMesa fiBacklogMesa)
{
int? result;
if (string.IsNullOrEmpty(fiBacklogMesa.PrioritySubset) || !int.TryParse(fiBacklogMesa.PrioritySubset[..1], out int prioritySubset) || prioritySubset == 0 || prioritySubset > 3)
result = null;
else
result = prioritySubset;
return result;
}
private static string GetIterationPath(string project, DateTime submittedDateTime) =>
submittedDateTime.Year != 2024 ? project : string.Concat(project, "\\", submittedDateTime.Year);
private static string GetTitle(FIBacklogMesa fiBacklogMesa)
{
string result = $"{fiBacklogMesa.Req} - {fiBacklogMesa.Subject.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)[0]}";
if (result.Length > 128)
result = result[..127];
return result;
}
private static string GetTitle(FIBacklogMesa fiBacklogMesa, ValueWithReq valueWithReq)
{
string result = $"{valueWithReq.Req} - {fiBacklogMesa.Subject.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)[0]}";
if (result.Length > 128)
result = result[..127];
return result;
}
private static string? GetMappedState(FIBacklogMesa fiBacklogMesa) =>
fiBacklogMesa.Status == "CMP" ? "Closed" : fiBacklogMesa.Status == "UAT" ? "Resolved" : fiBacklogMesa.Status == "In process" ? "Active" : null;
private static JsonPatchDocument GetBugDocument(string project, string site, ReadOnlyDictionary<string, string> assignedToNameToUser, ReadOnlyDictionary<string, string> requestorNameToUser, Task<WorkItem>? uatWorkItemTask, FIBacklogMesa fiBacklogMesa, DateTime submittedDateTime)
{
JsonPatchDocument result = [];
string title = GetTitle(fiBacklogMesa);
string iterationPath = GetIterationPath(project, submittedDateTime);
if (uatWorkItemTask?.Result.Id is not null)
AddPatch(result, "/relations/-", new WorkItemRelation() { Rel = "System.LinkTypes.Hierarchy-Forward", Url = uatWorkItemTask.Result.Url });
AddPatch(result, "/fields/System.AreaPath", string.Concat(project, "\\", site));
AddPatch(result, "/fields/System.IterationPath", iterationPath);
AddPatch(result, "/fields/System.Title", title);
AddPatch(result, "/fields/System.CreatedDate", submittedDateTime.AddHours(12).ToUniversalTime());
string? state = GetMappedState(fiBacklogMesa);
if (!string.IsNullOrEmpty(state))
AddPatch(result, "/fields/System.State", state);
if (!string.IsNullOrEmpty(fiBacklogMesa.Definition))
AddPatch(result, "/fields/System.Description", $"{fiBacklogMesa.Subject}<br>&nbsp;<br>{fiBacklogMesa.Definition}");
if (assignedToNameToUser.TryGetValue(fiBacklogMesa.AssignedTo, out string? assignedToUser))
AddPatch(result, "/fields/System.AssignedTo", assignedToUser);
if (requestorNameToUser.TryGetValue(fiBacklogMesa.Requestor, out string? requestorUser))
AddPatch(result, "/fields/Custom.Requester", requestorUser);
return result;
}
private static JsonPatchDocument GetFeatureDocument(string project, string site, ReadOnlyDictionary<string, string> requestorNameToUser, ReadOnlyDictionary<string, string> assignedToNameToUser, List<string> tags, Task<WorkItem>? userStoryWorkItemTask, FIBacklogMesa fiBacklogMesa, DateTime submittedDateTime)
{
JsonPatchDocument result = [];
string title = GetTitle(fiBacklogMesa);
int priority = GetPriority(fiBacklogMesa);
string? state = GetMappedState(fiBacklogMesa);
int? prioritySubset = GetPrioritySubset(fiBacklogMesa);
if (prioritySubset is not null)
AddPatch(result, "/fields/Microsoft.VSTS.Common.TimeCriticality", prioritySubset);
string iterationPath = GetIterationPath(project, submittedDateTime);
if (userStoryWorkItemTask?.Result.Id is not null)
AddPatch(result, "/relations/-", new WorkItemRelation() { Rel = "System.LinkTypes.Hierarchy-Forward", Url = userStoryWorkItemTask.Result.Url });
AddPatch(result, "/fields/System.AreaPath", string.Concat(project, "\\", site));
if (tags.Count > 0)
{
AddPatch(result, "/fields/System.Tags", tags.Last());
tags.RemoveAt(tags.Count - 1);
}
AddPatch(result, "/fields/System.IterationPath", iterationPath);
AddPatch(result, "/fields/Microsoft.VSTS.Common.Priority", priority);
if (!string.IsNullOrEmpty(fiBacklogMesa.Definition))
AddPatch(result, "/fields/System.Description", $"{fiBacklogMesa.Subject}<br>&nbsp;<br>{fiBacklogMesa.Definition}");
if (!string.IsNullOrEmpty(state))
AddPatch(result, "/fields/System.State", state);
if (!string.IsNullOrEmpty(fiBacklogMesa.EstEffortDays) && int.TryParse(fiBacklogMesa.EstEffortDays, out int estEffortDays) && estEffortDays != 0)
AddPatch(result, "/fields/Microsoft.VSTS.Scheduling.Effort", estEffortDays);
DateTime? dateTime = GetCommitDate(fiBacklogMesa);
if (dateTime is not null)
AddPatch(result, "/fields/Microsoft.VSTS.Scheduling.TargetDate", dateTime);
if (!string.IsNullOrEmpty(fiBacklogMesa.Updates))
AddPatch(result, "/fields/System.History", fiBacklogMesa.Updates);
AddPatch(result, "/fields/System.Title", title);
AddPatch(result, "/fields/System.CreatedDate", submittedDateTime.AddHours(12).ToUniversalTime());
if (assignedToNameToUser.TryGetValue(fiBacklogMesa.AssignedTo, out string? assignedToUser))
AddPatch(result, "/fields/System.AssignedTo", assignedToUser);
if (requestorNameToUser.TryGetValue(fiBacklogMesa.Requestor, out string? requestorUser))
AddPatch(result, "/fields/Custom.Requester", requestorUser);
// https://tfs.intra.infineon.com/tfs/ManufacturingIT/Mesa_FI/_apis/wit/workitemtypes/feature/fields?api-version=7.0
return result;
}
private static List<string> GetTags(FIBacklogMesa fiBacklogMesa)
{
List<string> results = [];
foreach (string tag in fiBacklogMesa.SystemS.Split('/'))
{
if (string.IsNullOrEmpty(tag.Trim()))
continue;
results.Add(tag.Trim());
}
return results;
}
private static List<string> GetTags(Fields fields)
{
List<string> results = [];
if (!string.IsNullOrEmpty(fields.SystemTags))
{
foreach (string tag in fields.SystemTags.Split(';'))
{
if (string.IsNullOrEmpty(tag.Trim()))
continue;
results.Add(tag.Trim());
}
}
return results;
}
private static void CreateWorkItem(WorkItemTrackingHttpClient workItemTrackingHttpClient, string project, string site, ReadOnlyDictionary<string, string> assignedToNameToUser, ReadOnlyDictionary<string, string> requestorNameToUser, FIBacklogMesa fiBacklogMesa)
{
DateTime submittedDateTime;
JsonPatchDocument tagDocument;
Task<WorkItem>? workItem = null;
List<string> tags = GetTags(fiBacklogMesa);
bool isBugFix = fiBacklogMesa.Priority == "0 - BugFix";
if (assignedToNameToUser.Count > requestorNameToUser.Count)
throw new Exception();
if (!DateTime.TryParse(fiBacklogMesa.Submitted, out submittedDateTime))
submittedDateTime = DateTime.MinValue;
if (isBugFix)
{
Task<WorkItem>? uatWorkItemTask = null;
JsonPatchDocument bugDocument = GetBugDocument(project, site, requestorNameToUser, assignedToNameToUser, uatWorkItemTask, fiBacklogMesa, submittedDateTime);
workItem = workItemTrackingHttpClient.CreateWorkItemAsync(bugDocument, project, "Bug");
workItem.Wait();
}
if (!isBugFix)
{
Task<WorkItem>? userStoryWorkItemTask = null;
JsonPatchDocument featureDocument = GetFeatureDocument(project, site, requestorNameToUser, assignedToNameToUser, tags, userStoryWorkItemTask, fiBacklogMesa, submittedDateTime);
workItem = workItemTrackingHttpClient.CreateWorkItemAsync(featureDocument, project, "Feature");
workItem.Wait();
}
for (int i = tags.Count - 1; i > -1; i--)
{
if (workItem is null)
continue;
if (workItem.Result.Id is null)
throw new NotSupportedException();
tagDocument = [];
AddPatch(tagDocument, "/fields/System.Tags", tags[i]);
tags.RemoveAt(i);
workItem = workItemTrackingHttpClient.UpdateWorkItemAsync(tagDocument, workItem.Result.Id.Value);
workItem.Wait();
}
}
private static void KillTime(int loops)
{
for (int i = 1; i < loops; i++)
Thread.Sleep(500);
}
private static void Update(HttpClient httpClient, string basePage, string api, string query, HttpContent httpContent)
{
#if Windows
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.PatchAsync(string.Concat(basePage, api, query), httpContent);
httpResponseMessageTask.Wait();
if (!httpResponseMessageTask.Result.IsSuccessStatusCode)
throw new Exception(httpResponseMessageTask.Result.StatusCode.ToString());
Task<string> stringTask = httpResponseMessageTask.Result.Content.ReadAsStringAsync();
stringTask.Wait();
#endif
KillTime(30);
}
private static void Update(HttpClient httpClient, string basePage, string api, WorkItemTrackingHttpClient workItemTrackingHttpClient, string sync, ValueWithReq valueWithReq)
{
JsonPatchDocument result = [];
AddPatch(result, "/fields/System.Tags", sync);
Task<WorkItem> workItem = workItemTrackingHttpClient.UpdateWorkItemAsync(result, valueWithReq.Value.Id);
workItem.Wait();
if (result is null)
{
var payload = new
{
op = "replace",
path = "/fields/System.IterationPath",
value = "Mesa_FI"
};
string stringPayload = JsonSerializer.Serialize(payload);
HttpContent httpContent = new StringContent($"[{stringPayload}]", Encoding.UTF8, "application/json-patch+json");
Update(httpClient, basePage, api, $"/workitems/{valueWithReq.Value.Id}?api-version=1.0", httpContent);
}
}
private static int SetSyncTag(HttpClient httpClient,
string basePage,
string api,
WorkItemTrackingHttpClient workItemTrackingHttpClient,
ReadOnlyDictionary<string, string> assignedToNameToUser,
ReadOnlyDictionary<string, string> requestorNameToUser,
Dictionary<string, FIBacklogMesa> keyToFIBacklogMesa,
ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
{
int result = 0;
string key;
string title;
int priority;
bool isBugFix;
string? state;
List<string> tags;
TimeSpan timeSpan;
DateTime? dateTime;
List<string> compareTags;
const string sync = "Sync";
FIBacklogMesa? fiBacklogMesa;
foreach (ValueWithReq valueWithReq in valueWithReqCollection)
{
key = $"{valueWithReq.Req} - ";
if (!string.IsNullOrEmpty(key)) // Forced to skip logic
continue;
compareTags = GetTags(valueWithReq.Value.Fields);
if (compareTags.Contains(sync))
continue;
if (!keyToFIBacklogMesa.TryGetValue(key, out fiBacklogMesa))
continue;
tags = GetTags(fiBacklogMesa);
title = GetTitle(fiBacklogMesa, valueWithReq);
isBugFix = fiBacklogMesa.Priority == "0 - BugFix";
_ = requestorNameToUser.TryGetValue(fiBacklogMesa.Requestor, out string? requestorUser);
if (!string.IsNullOrEmpty(requestorUser) && (valueWithReq.Value.Fields.CustomRequester is null || !valueWithReq.Value.Fields.CustomRequester.UniqueName.Equals(requestorUser, StringComparison.CurrentCultureIgnoreCase)))
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
_ = assignedToNameToUser.TryGetValue(fiBacklogMesa.Requestor, out string? assignedToUser);
if (!string.IsNullOrEmpty(assignedToUser) && (valueWithReq.Value.Fields.SystemAssignedTo is null || !valueWithReq.Value.Fields.SystemAssignedTo.UniqueName.Equals(assignedToUser, StringComparison.CurrentCultureIgnoreCase)))
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
if (valueWithReq.Value.Fields.SystemTitle != title)
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
foreach (string tag in tags)
{
if (compareTags.Contains(tag))
continue;
_ = tags.Remove(tag);
break;
}
if (tags.Count != compareTags.Count)
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
if ((isBugFix && valueWithReq.Value.Fields.SystemWorkItemType != "Bug") || (!isBugFix && valueWithReq.Value.Fields.SystemWorkItemType == "Bug"))
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
if (!isBugFix)
{
priority = GetPriority(fiBacklogMesa);
if (valueWithReq.Value.Fields.MicrosoftVSTSCommonPriority != priority)
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
}
state = GetMappedState(fiBacklogMesa);
if (!string.IsNullOrEmpty(state) && valueWithReq.Value.Fields.SystemState != state)
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
if (!isBugFix && int.TryParse(fiBacklogMesa.EstEffortDays, out int estEffortDays) && valueWithReq.Value.Fields.MicrosoftVSTSSchedulingEffort != estEffortDays)
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
dateTime = GetCommitDate(fiBacklogMesa);
if (dateTime is not null)
{
timeSpan = new(valueWithReq.Value.Fields.MicrosoftVSTSSchedulingTargetDate.Ticks - dateTime.Value.Ticks);
if (timeSpan.Hours is > 32 or < -32)
{
result += 1;
Update(httpClient, basePage, api, workItemTrackingHttpClient, sync, valueWithReq);
continue;
}
}
}
return result;
}
private static void CreateWorkItems(HttpClient httpClient,
string sourceDirectory,
string basePage,
string api,
string query,
WorkItemTrackingHttpClient workItemTrackingHttpClient,
string project,
string site,
ReadOnlyDictionary<string, string> assignedToNameToUser,
ReadOnlyDictionary<string, string> requestorNameToUser,
string json)
{
int counter = 0;
string ids = GetIds(httpClient, basePage, api, query);
Dictionary<string, FIBacklogMesa> keyToFIBacklogMesa = GetFIBacklogMesaCollection(json);
ReadOnlyCollection<ValueWithReq> valueWithReqCollection = string.IsNullOrEmpty(ids) ? new([]) : GetWorkItems(httpClient, basePage, api, sourceDirectory, ids);
int updated = SetSyncTag(httpClient, basePage, api, workItemTrackingHttpClient, assignedToNameToUser, requestorNameToUser, keyToFIBacklogMesa, valueWithReqCollection);
if (updated == 0)
{
UpdateAllFeaturesNotArtSPS(httpClient, basePage, api, valueWithReqCollection);
UpdateAllWorkItemsPharesComment(httpClient, basePage, api, sourceDirectory, keyToFIBacklogMesa, valueWithReqCollection);
ReadOnlyCollection<ValueWithReq> extra = RemoveFrom(keyToFIBacklogMesa, valueWithReqCollection);
foreach (KeyValuePair<string, FIBacklogMesa> keyValuePair in keyToFIBacklogMesa)
{
if (keyToFIBacklogMesa.Count == extra.Count)
break;
if (keyValuePair.Value.Status is "CMP" or "CNCL")
continue;
CreateWorkItem(workItemTrackingHttpClient, project, site, assignedToNameToUser, requestorNameToUser, keyValuePair.Value);
counter++;
}
}
}
private static void CreateWorkItems(ILogger<Worker> logger, string sourceDirectory, string api, string site, string query, string project, string basePage, string baseAddress, byte[] bytes, string[] assignedToNames, string[] requestorNames, string reportFullPath, MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeaderValue, WorkItemTrackingHttpClient workItemTrackingHttpClient, HttpClient httpClient)
{
string base64 = Convert.ToBase64String(bytes);
string json = File.ReadAllText(reportFullPath);
httpClient.DefaultRequestHeaders.Authorization = new("Basic", base64);
httpClient.DefaultRequestHeaders.Accept.Add(mediaTypeWithQualityHeaderValue);
ReadOnlyDictionary<string, string> requestorNameToUser = GetRequestorNameToUser(requestorNames);
ReadOnlyDictionary<string, string> assignedToNameToUser = GetAssignedToNameToUser(assignedToNames);
logger.LogInformation("{baseAddress}{basePage}/{project}{api}{query}", baseAddress, basePage, HttpUtility.HtmlEncode(project), api, query);
CreateWorkItems(httpClient, sourceDirectory, basePage, api, query, workItemTrackingHttpClient, project, site, new(assignedToNameToUser), new(requestorNameToUser), json);
}
private static ReadOnlyDictionary<string, string> GetAssignedToNameToUser(string[] assignedToNames)
{
Dictionary<string, string> results = [];
string[] segments;
foreach (string assignedToName in assignedToNames)
{
segments = assignedToName.Split('|');
if (segments.Length != 2)
continue;
results.Add(segments[0], segments[1]);
}
return new(results);
}
private static ReadOnlyDictionary<string, string> GetRequestorNameToUser(string[] requestorNames)
{
Dictionary<string, string> results = [];
string[] segments;
foreach (string requestorName in requestorNames)
{
segments = requestorName.Split('|');
if (segments.Length != 2)
continue;
results.Add(segments[0], segments[1]);
}
return new(results);
}
internal static void CreateWorkItems(ILogger<Worker> logger, List<string> args)
{
string api = args[6];
string pat = args[8];
string site = args[2];
string query = args[7];
string project = args[5];
string basePage = args[4];
string baseAddress = args[3];
string sourceDirectory = args[0];
VssBasicCredential credential = new("", pat);
string[] requestorNames = args[11].Split(',');
string[] assignedToNames = args[10].Split(',');
byte[] bytes = Encoding.ASCII.GetBytes($":{pat}");
string reportFullPath = Path.GetFullPath(Path.Combine(sourceDirectory, args[9]));
VssConnection connection = new(new(string.Concat(baseAddress, basePage)), credential);
MediaTypeWithQualityHeaderValue mediaTypeWithQualityHeaderValue = new("application/json");
WorkItemTrackingHttpClient workItemTrackingHttpClient = connection.GetClient<WorkItemTrackingHttpClient>();
HttpClient httpClient = new(new HttpClientHandler() { UseDefaultCredentials = true }) { BaseAddress = new(baseAddress) };
CreateWorkItems(logger, sourceDirectory, api, site, query, project, basePage, baseAddress, bytes, assignedToNames, requestorNames, reportFullPath, mediaTypeWithQualityHeaderValue, workItemTrackingHttpClient, httpClient);
}
}
#endif

View File

@ -1,427 +0,0 @@
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 Attributes);
public record Record(WorkItem WorkItem, ReadOnlyDictionary<int, Record> Children);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Record))]
internal partial class RecordCommonSourceGenerationContext : JsonSerializerContext
{
}
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<int, WorkItem> GetWorkItems(string filterDirectory, string[] files)
{
Dictionary<int, WorkItem> 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?.Attributes is null || relation.Attributes.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<int, Record> GetKeyValuePairs(ReadOnlyDictionary<int, WorkItem> workItems, WorkItem workItem)
{
Dictionary<int, Record> results = [];
int? childId;
WorkItem? childWorkItem;
List<WorkItem> collection = [];
Dictionary<int, Record> 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<int, Record> GetWorkItemAndChildren(ReadOnlyDictionary<int, WorkItem> workItems)
{
Dictionary<int, Record> results = [];
Dictionary<int, Record> keyValuePairs;
foreach (KeyValuePair<int, WorkItem> keyValuePair in workItems)
{
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<char> spaces, WorkItem workItem, KeyValuePair<int, Record> keyValuePair, bool condensed, bool sprintOnly) =>
sprintOnly ? $"\t- [ ] {workItem.IterationPath}" :
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<char> spaces, List<string> lines, Record record, bool condensed, bool sprintOnly)
{
string line;
spaces.Add('\t');
WorkItem workItem;
foreach (KeyValuePair<int, Record> keyValuePair in record.Children)
{
workItem = keyValuePair.Value.WorkItem;
line = GetLine(spaces, workItem, keyValuePair, condensed, sprintOnly).TrimEnd();
lines.Add(line);
AppendLines(spaces, lines, keyValuePair.Value, condensed, sprintOnly);
}
spaces.RemoveAt(0);
}
private static void AppendLines(string url, List<char> spaces, List<string> lines, ReadOnlyDictionary<int, Record> workItemAndChildren, string workItemType)
{
List<string> results = [];
WorkItem workItem;
string? maxIterationPath;
List<string> distinct = [];
foreach (KeyValuePair<int, Record> keyValuePair in workItemAndChildren)
{
workItem = keyValuePair.Value.WorkItem;
// if (keyValuePair.Key != 109724)
// continue;
if (workItem.WorkItemType != workItemType)
continue;
results.Add($"## {workItem.AssignedTo} - {workItem.Id} - {workItem.Title}");
results.Add(string.Empty);
results.Add($"- [{workItem.Id}]({url}{workItem.Id})");
if (keyValuePair.Value.Children.Count == 0)
results.Add(string.Empty);
else
{
AppendLines(spaces, results, keyValuePair.Value, condensed: true, sprintOnly: false);
results.Add(string.Empty);
distinct.Clear();
AppendLines(spaces, distinct, keyValuePair.Value, condensed: false, sprintOnly: true);
if (distinct.Count > 1)
{
results.Add($"## Distinct Iteration Path(s) - {workItem.WorkItemType} - {workItem.AssignedTo} - {workItem.Id} - {workItem.Title} - {workItem.IterationPath}");
results.Add(string.Empty);
results.Add($"- [{workItem.Id}]({url}{workItem.Id})");
distinct.Sort();
distinct = (from l in distinct select l.Trim()).Distinct().ToList();
results.AddRange(distinct);
results.Add(string.Empty);
maxIterationPath = distinct.Max();
if (!string.IsNullOrEmpty(maxIterationPath) && maxIterationPath.Contains("] ") && maxIterationPath.Split(']')[1].Trim() != workItem.IterationPath)
{
results.Add($"### Sync to Distinct Max Iteration Path => {maxIterationPath} - {workItem.Id} - {workItem.Title}");
results.Add(string.Empty);
}
}
results.Add($"## Extended - {workItem.Id} - {workItem.Title}");
results.Add(string.Empty);
AppendLines(spaces, results, keyValuePair.Value, condensed: false, sprintOnly: false);
results.Add(string.Empty);
}
lines.AddRange(results);
results.Clear();
}
}
private static void WriteFiles(string destinationDirectory, string fileName, ReadOnlyCollection<string> lines)
{
string text = string.Join(Environment.NewLine, lines);
string markdownFile = Path.Combine(destinationDirectory, $"{fileName}.md");
string textOld = !File.Exists(markdownFile) ? string.Empty : File.ReadAllText(markdownFile);
if (text != textOld)
File.WriteAllText(markdownFile, text);
string html = CommonMark.CommonMarkConverter.Convert(text);
string htmlFile = Path.Combine(destinationDirectory, $"{fileName}.html");
string htmlOld = !File.Exists(htmlFile) ? string.Empty : File.ReadAllText(htmlFile);
if (html != htmlOld)
File.WriteAllText(htmlFile, html);
}
private static ReadOnlyCollection<WorkItem> FilterChildren(Record record, ReadOnlyCollection<string> workItemTypes)
{
List<WorkItem> results = [];
WorkItem workItem;
foreach (KeyValuePair<int, Record> keyValuePair in record.Children)
{
workItem = keyValuePair.Value.WorkItem;
if (!workItemTypes.Contains(workItem.WorkItemType))
continue;
results.Add(workItem);
}
return new(results);
}
private static string? GetMaxIterationPath(ReadOnlyCollection<WorkItem> workItems)
{
string? result;
List<string> results = [];
foreach (WorkItem workItem in workItems)
{
if (results.Contains(workItem.IterationPath))
continue;
results.Add(workItem.IterationPath);
}
result = results.Count == 0 ? null : results.Max();
return result;
}
private static void FeatureCheckIterationPath(string url, List<string> lines, ReadOnlyCollection<string> workItemTypes, ReadOnlyDictionary<int, Record> workItemAndChildren, string workItemType)
{
WorkItem workItem;
string? maxIterationPath;
List<string> results = [];
ReadOnlyCollection<WorkItem> workItems;
foreach (KeyValuePair<int, Record> keyValuePair in workItemAndChildren)
{
workItem = keyValuePair.Value.WorkItem;
if (workItem.WorkItemType != workItemType)
continue;
results.Clear();
if (keyValuePair.Value.Children.Count == 0)
continue;
workItems = FilterChildren(keyValuePair.Value, workItemTypes);
maxIterationPath = GetMaxIterationPath(workItems);
if (string.IsNullOrEmpty(maxIterationPath) || workItem.IterationPath == maxIterationPath)
continue;
results.Add($"## {workItem.AssignedTo} - {workItem.Id} - {workItem.Title}");
results.Add(string.Empty);
results.Add($"- [{workItem.Id}]({url}{workItem.Id})");
results.Add($"- [ ] {workItem.Id} => {workItem.IterationPath} != {maxIterationPath}");
results.Add(string.Empty);
lines.AddRange(results);
}
}
private static ReadOnlyCollection<int> GetIdsNotMatching(string tags, ReadOnlyCollection<WorkItem> workItems)
{
List<int> results = [];
string[] segments;
string[] parentTags = tags.Split(';');
foreach (WorkItem workItem in workItems)
{
segments = tags.Split(';');
if (segments.Length > 0 && parentTags.Any(l => segments.Contains(l)))
continue;
results.Add(workItem.Id);
}
return new(results);
}
private static void FeatureCheckTag(string url, List<string> lines, ReadOnlyCollection<string> workItemTypes, ReadOnlyDictionary<int, Record> workItemAndChildren, string workItemType)
{
WorkItem workItem;
List<string> results = [];
ReadOnlyCollection<int> idsNotMatching;
ReadOnlyCollection<WorkItem> workItems;
foreach (KeyValuePair<int, Record> keyValuePair in workItemAndChildren)
{
workItem = keyValuePair.Value.WorkItem;
if (workItem.WorkItemType != workItemType)
continue;
results.Clear();
if (keyValuePair.Value.Children.Count == 0)
continue;
if (string.IsNullOrEmpty(workItem.Tags))
idsNotMatching = new([workItem.Id]);
else
{
workItems = FilterChildren(keyValuePair.Value, workItemTypes);
idsNotMatching = GetIdsNotMatching(workItem.Tags, workItems);
if (!string.IsNullOrEmpty(workItem.Tags) && idsNotMatching.Count == 0)
continue;
}
results.Add($"## {workItem.AssignedTo} - {workItem.Id} - {workItem.Title}");
results.Add(string.Empty);
results.Add($"- [{workItem.Id}]({url}{workItem.Id})");
foreach (int id in idsNotMatching)
results.Add($"- [ ] {id} {nameof(workItem.Tags)} != {workItem.Tags}");
results.Add(string.Empty);
lines.AddRange(results);
}
}
private static ReadOnlyCollection<int> GetIdsNotMatching(int? priority, ReadOnlyCollection<WorkItem> workItems)
{
List<int> results = [];
foreach (WorkItem workItem in workItems)
{
if (workItem.Priority == priority)
continue;
results.Add(workItem.Id);
}
return new(results);
}
private static void FeatureCheckPriority(string url, List<string> lines, ReadOnlyCollection<string> workItemTypes, ReadOnlyDictionary<int, Record> workItemAndChildren, string workItemType)
{
WorkItem workItem;
List<string> results = [];
ReadOnlyCollection<int> idsNotMatching;
ReadOnlyCollection<WorkItem> workItems;
foreach (KeyValuePair<int, Record> keyValuePair in workItemAndChildren)
{
workItem = keyValuePair.Value.WorkItem;
if (workItem.WorkItemType != workItemType)
continue;
results.Clear();
if (keyValuePair.Value.Children.Count == 0)
continue;
workItems = FilterChildren(keyValuePair.Value, workItemTypes);
idsNotMatching = GetIdsNotMatching(workItem.Priority, workItems);
if (idsNotMatching.Count == 0)
continue;
results.Add($"## {workItem.AssignedTo} - {workItem.Id} - {workItem.Title}");
results.Add(string.Empty);
results.Add($"- [{workItem.Id}]({url}{workItem.Id})");
foreach (int id in idsNotMatching)
results.Add($"- [ ] {id} {nameof(workItem.Priority)} != {workItem.Priority}");
results.Add(string.Empty);
lines.AddRange(results);
}
}
internal static void WriteMarkdown(ILogger<Worker> logger, List<string> args)
{
string url = args[6];
List<char> spaces = [];
List<string> lines = [];
string searchPattern = args[2];
string filterDirectory = args[3];
string[] workItemTypes = args[4].Split('|');
string sourceDirectory = Path.GetFullPath(args[0]);
string destinationDirectory = Path.GetFullPath(args[5]);
if (!Directory.Exists(destinationDirectory))
_ = Directory.CreateDirectory(destinationDirectory);
ReadOnlyCollection<string> userStoryWorkItemTypes = new(["User Story"]);
ReadOnlyCollection<string> userStoryTaskWorkItemTypes = new(["User Story", "Task"]);
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
logger.LogInformation("With search pattern '{SearchPattern}' found {files} file(s)", searchPattern, files.Length);
ReadOnlyDictionary<int, WorkItem> workItems = GetWorkItems(filterDirectory, files);
logger.LogInformation("With search pattern '{SearchPattern}' found {files} workItem(s)", searchPattern, workItems.Count);
ReadOnlyDictionary<int, Record> workItemAndChildren = GetWorkItemAndChildren(workItems);
logger.LogInformation("With search pattern '{SearchPattern}' found {files} workItemAndChildren", searchPattern, workItemAndChildren.Count);
if (workItemAndChildren.Count == -1)
{
string json = JsonSerializer.Serialize(workItemAndChildren, RecordCommonSourceGenerationContext.Default.ReadOnlyDictionaryInt32Record);
File.WriteAllText(".json", json);
}
foreach (string workItemType in workItemTypes)
{
lines.Clear();
lines.Add("# WorkItems");
lines.Add(string.Empty);
AppendLines(url, spaces, lines, workItemAndChildren, workItemType);
WriteFiles(destinationDirectory, workItemType, new(lines));
}
{
lines.Clear();
string workItemType = "Feature";
lines.Add($"# {nameof(FeatureCheckIterationPath)}");
lines.Add(string.Empty);
FeatureCheckIterationPath(url, lines, userStoryTaskWorkItemTypes, workItemAndChildren, workItemType);
WriteFiles(destinationDirectory, $"{nameof(FeatureCheckIterationPath)}", new(lines));
}
{
lines.Clear();
string workItemType = "Feature";
lines.Add($"# {nameof(FeatureCheckTag)}");
lines.Add(string.Empty);
FeatureCheckTag(url, lines, userStoryWorkItemTypes, workItemAndChildren, workItemType);
WriteFiles(destinationDirectory, $"{nameof(FeatureCheckTag)}", new(lines));
}
{
lines.Clear();
string workItemType = "Feature";
lines.Add($"# {nameof(FeatureCheckPriority)}");
lines.Add(string.Empty);
FeatureCheckPriority(url, lines, userStoryWorkItemTypes, workItemAndChildren, workItemType);
WriteFiles(destinationDirectory, $"{nameof(FeatureCheckPriority)}", new(lines));
}
}
}