Moved to ADO2024 PI#

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

View File

@ -0,0 +1,146 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.ConvertExcelToJson;
public class FIBacklogMesa
{
[JsonConstructor]
public FIBacklogMesa(string req,
string submitted,
string requestor,
string assignedTo,
string secondResource,
string subject,
string systemS,
string priority,
string training,
string prioritySubset,
string status,
string definition,
string updates,
string estEffortDays,
string commitDate,
string reCommitDate,
string uATAsOf,
string cmpDate,
string f20,
string f21,
string f22,
string f23,
string f24,
string f25,
string f26,
string f27,
string f28,
string f29,
string f30,
string f31,
string f32,
string f33)
{
Req = req;
Submitted = submitted;
Requestor = requestor;
AssignedTo = assignedTo;
SecondResource = secondResource;
Subject = subject;
SystemS = systemS;
Priority = priority;
Training = training;
PrioritySubset = prioritySubset;
Status = status;
Definition = definition;
Updates = updates;
EstEffortDays = estEffortDays;
CommitDate = commitDate;
ReCommitDate = reCommitDate;
UATAsOf = uATAsOf;
CMPDate = cmpDate;
F20 = f20;
F21 = f21;
F22 = f22;
F23 = f23;
F24 = f24;
F25 = f25;
F26 = f26;
F27 = f27;
F28 = f28;
F29 = f29;
F30 = f30;
F31 = f31;
F32 = f32;
F33 = f33;
}
public string Req { get; set; } // { init; get; }
public string Submitted { get; set; } // { init; get; }
public string Requestor { get; set; } // { init; get; }
[JsonPropertyName("Assigned To")]
public string AssignedTo { get; set; } // { init; get; }
[JsonPropertyName("Second Resource")]
public string SecondResource { get; set; } // { init; get; }
[JsonPropertyName("Subject - from Requestor")]
public string Subject { get; set; } // { init; get; }
[JsonPropertyName("System(s)")]
public string SystemS { get; set; } // { init; get; }
public string Priority { get; set; } // { init; get; }
[JsonPropertyName("Spec/ECN/Training")]
public string Training { get; set; } // { init; get; }
[JsonPropertyName("Qual/Eff")]
public string PrioritySubset { get; set; } // { init; get; }
public string Status { get; set; } // { init; get; }
[JsonPropertyName("Definition - from FI")]
public string Definition { get; set; } // { init; get; }
public string Updates { get; set; } // { init; get; }
[JsonPropertyName("Est Effort _(days)")]
public string EstEffortDays { get; set; } // { init; get; }
[JsonPropertyName("Commit Date")]
public string CommitDate { get; set; } // { init; get; }
[JsonPropertyName("Re-Commit Date")]
public string ReCommitDate { get; set; } // { init; get; }
[JsonPropertyName("UAT as of")]
public string UATAsOf { get; set; } // { init; get; }
[JsonPropertyName("CMP _Date")]
public string CMPDate { get; set; } // { init; get; }
public string F20 { get; set; } // { init; get; }
public string F21 { get; set; } // { init; get; }
public string F22 { get; set; } // { init; get; }
public string F23 { get; set; } // { init; get; }
public string F24 { get; set; } // { init; get; }
public string F25 { get; set; } // { init; get; }
public string F26 { get; set; } // { init; get; }
public string F27 { get; set; } // { init; get; }
public string F28 { get; set; } // { init; get; }
public string F29 { get; set; } // { init; get; }
public string F30 { get; set; } // { init; get; }
public string F31 { get; set; } // { init; get; }
public string F32 { get; set; } // { init; get; }
public string F33 { get; set; } // { init; get; }
}
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNameCaseInsensitive = true)]
[JsonSerializable(typeof(FIBacklogMesa[]))]
internal partial class FIBacklogMesaCollectionSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNameCaseInsensitive = true)]
[JsonSerializable(typeof(FIBacklogMesa))]
internal partial class FIBacklogMesaSourceGenerationContext : JsonSerializerContext
{
}
#endif

View File

@ -0,0 +1,93 @@
using Microsoft.Extensions.Logging;
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;
string old;
string from;
string what;
string concat;
string checkFile;
string extension;
string[] matches;
string[] segments;
string environment;
List<string> distinct = [];
string globalSettingsLines = File.ReadAllText(globalSettingsFile);
foreach (string line in lines)
{
segments = line.Split('|');
if (segments.Length != 7)
{
logger.LogWarning("Wrong length!");
continue;
}
to = segments[5].Trim();
old = segments[1].Trim();
from = segments[4].Trim();
what = segments[3].Trim();
environment = segments[2].Trim();
concat = string.Concat(environment, '-', what, '-', from, '-', to);
if (distinct.Contains(concat))
{
logger.LogWarning("Not distinct <{concat}>!", concat);
continue;
}
distinct.Add(concat);
globalSettingsLines = globalSettingsLines.Replace(old, concat);
matches = (from l in files where l.Contains(old) select l).ToArray();
if (matches.Length != 1)
{
logger.LogWarning("matches == {matchesLength} <{concat}>!", matches.Length, concat);
continue;
}
extension = Path.GetExtension(matches[0]);
checkFile = Path.Combine(sourceDirectory, $"{concat}{extension}");
File.Move(matches[0], checkFile);
}
File.WriteAllText(globalSettingsFile, globalSettingsLines);
}
internal static void RenameFiles(ILogger<Worker> logger, List<string> args)
{
string find = args[6];
string replace = args[7];
string sourceDirectory = Path.GetFullPath(args[0]);
string globalSettingsFile = Path.GetFullPath(args[4]);
string[] files = Directory.GetFiles(sourceDirectory, args[2], SearchOption.TopDirectoryOnly);
string[] confFiles = Directory.GetFiles(sourceDirectory, $"*{find}", SearchOption.TopDirectoryOnly);
string[] ignoreFileNames = args[8].Split(',').Select(l => Path.Combine(sourceDirectory, l)).ToArray();
RenameFiles(find, replace, ignoreFileNames, confFiles);
string checkFile = Path.Combine(sourceDirectory, args[3]);
if (files.Length == 0 || !File.Exists(checkFile))
logger.LogWarning("No found!");
else
{
string[] lines = File.ReadAllLines(checkFile);
if (lines.Length == 0)
logger.LogWarning("No lines!");
else
RenameFiles(logger, sourceDirectory, files, lines, globalSettingsFile);
}
}
}

View File

@ -0,0 +1,57 @@
using Microsoft.Extensions.Logging;
using System.Globalization;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240806
{
private static void TryArchiveFiles(string sourceDirectory, string pattern, string archiveDirectory, int minimumLength, int days)
{
string checkFile;
FileInfo fileInfo;
string weekOfYear;
string checkDirectory;
string[] directorySegments;
DateTime dateTime = DateTime.Now.AddDays(-days);
Calendar calendar = new CultureInfo("en-US").Calendar;
string[] sourceDirectorySegments = sourceDirectory.Split(Path.DirectorySeparatorChar);
string[] files = Directory.GetFiles(sourceDirectory, pattern, SearchOption.AllDirectories);
if (sourceDirectorySegments.Length < 1)
throw new Exception("Can't be root drive!");
foreach (string file in files)
{
fileInfo = new FileInfo(file);
if (string.IsNullOrEmpty(fileInfo.DirectoryName) || fileInfo.IsReadOnly || fileInfo.Length < minimumLength || fileInfo.LastWriteTime < dateTime)
continue;
directorySegments = fileInfo.DirectoryName.Split(Path.DirectorySeparatorChar);
if (directorySegments.Length < sourceDirectorySegments.Length)
continue;
weekOfYear = $"{fileInfo.LastWriteTime.Year}_Week_{calendar.GetWeekOfYear(fileInfo.LastWriteTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}";
checkDirectory = string.Concat(archiveDirectory, Path.DirectorySeparatorChar, weekOfYear);
for (int i = sourceDirectorySegments.Length; i < directorySegments.Length; i++)
checkDirectory = string.Concat(checkDirectory, Path.DirectorySeparatorChar, directorySegments[i]);
checkDirectory = string.Concat(checkDirectory, Path.DirectorySeparatorChar, fileInfo.LastWriteTime.ToString("yyyy-MM-dd"));
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
checkFile = Path.Combine(checkDirectory, string.Concat(fileInfo.LastWriteTime.ToString("HH-mm-ss-fff"), "~", fileInfo.Name));
if (File.Exists(checkFile))
continue;
File.Move(fileInfo.FullName, checkFile);
}
}
internal static void ArchiveFiles(ILogger<Worker> logger, List<string> args)
{
string pattern = args[4];
int days = int.Parse(args[6]);
logger.LogInformation("Hello");
string sourceDirectory = args[0];
int minimumLength = int.Parse(args[5]);
int millisecondsDelay = int.Parse(args[2]);
string archiveDirectory = Path.GetFullPath(args[7]);
TryArchiveFiles(sourceDirectory, pattern, archiveDirectory, minimumLength, days);
Thread.Sleep(millisecondsDelay);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
using Microsoft.Extensions.Logging;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240820
{
internal static void MoveFilesWithSleep(ILogger<Worker> logger, List<string> args)
{
string checkFile;
string checkDirectory;
int sleep = int.Parse(args[4]);
string searchPattern = args[3];
string sourceDirectory = args[0];
string destinationDirectory = args[2];
string source = Path.GetFullPath(sourceDirectory);
FileInfo[] collection = Directory.GetFiles(source, "*", SearchOption.TopDirectoryOnly).Select(l => new FileInfo(l)).ToArray();
string[] files = (from l in collection orderby l.LastWriteTime select l.FullName).ToArray();
logger.LogInformation("With search pattern '{SearchPattern}' found {files}", searchPattern, files.Length);
foreach (string file in files)
{
Thread.Sleep(500);
checkFile = file.Replace(source, destinationDirectory);
if (checkFile == file)
throw new NotSupportedException("Replace failed!");
checkDirectory = Path.GetDirectoryName(checkFile) ?? throw new NotSupportedException();
try
{
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
Thread.Sleep(sleep);
}
catch (Exception ex)
{ logger.LogInformation(ex, "Inner loop error!"); }
}
}
}

View File

@ -0,0 +1,228 @@
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 Helper20240822
{
public record Record(string? Title, ReadOnlyCollection<string> Tags, string? Completed);
public record Root([property: JsonPropertyName("headings")] Heading[] Headings,
[property: JsonPropertyName("lanes")] Lane[] Lanes);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Root))]
internal partial class Helper20240822RootSourceGenerationContext : JsonSerializerContext
{
}
public record Welcome2([property: JsonPropertyName("headings")] Heading[] Headings,
[property: JsonPropertyName("lanes")] Lane[] Lanes);
public record Heading([property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("heading")] string HeadingHeading);
public record Lane([property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("columns")] Column[][] Columns);
public record Column([property: JsonPropertyName("id")] string Id,
[property: JsonPropertyName("name")] string Name,
[property: JsonPropertyName("description")] string Description,
[property: JsonPropertyName("metadata")] Metadata? Metadata,
[property: JsonPropertyName("subTasks")] SubTask[]? SubTasks,
[property: JsonPropertyName("relations")] object[] Relations,
[property: JsonPropertyName("comments")] Comment[] Comments,
[property: JsonPropertyName("column")] string ColumnColumn,
[property: JsonPropertyName("workload")] long Workload,
[property: JsonPropertyName("progress")] long Progress,
[property: JsonPropertyName("remainingWorkload")] long RemainingWorkload,
[property: JsonPropertyName("dueData")] DueData DueData);
public record Comment([property: JsonPropertyName("text")] string Text,
[property: JsonPropertyName("date")] DateTimeOffset Date);
public record DueData([property: JsonPropertyName("completed")] bool Completed,
[property: JsonPropertyName("completedDate")] object CompletedDate,
[property: JsonPropertyName("dueDate")] DateTimeOffset DueDate,
[property: JsonPropertyName("overdue")] bool Overdue,
[property: JsonPropertyName("dueDelta")] long DueDelta,
[property: JsonPropertyName("dueMessage")] string DueMessage);
public record Metadata([property: JsonPropertyName("assigned")] string Assigned,
[property: JsonPropertyName("created")] DateTimeOffset Created,
[property: JsonPropertyName("progress")] long? Progress,
[property: JsonPropertyName("started")] DateTimeOffset? Started,
[property: JsonPropertyName("status")] string? Status,
[property: JsonPropertyName("tags")] string[]? Tags,
[property: JsonPropertyName("type")] string? Type,
[property: JsonPropertyName("updated")] DateTimeOffset Updated,
[property: JsonPropertyName("due")] DateTimeOffset? Due,
[property: JsonPropertyName("completed")] DateTimeOffset? Completed);
public record SubTask([property: JsonPropertyName("text")] string Text,
[property: JsonPropertyName("completed")] bool Completed);
private static ReadOnlyCollection<ReadOnlyCollection<Record>> GetRecords(Column[][] columnCollection)
{
List<ReadOnlyCollection<Record>> results = [];
bool check;
int subTasks;
Column column;
int completed;
List<Record> row;
string? subtasks;
List<string> tags;
for (int i = 0; i < int.MaxValue; i++)
{
row = [];
check = false;
foreach (Column[] columns in columnCollection)
{
if (columns.Length <= i)
row.Add(new(null, new([]), null));
else
{
tags = [];
subTasks = 0;
completed = 0;
column = columns[i];
if (!check)
check = true;
if (column.Metadata?.Tags is not null && column.Metadata.Tags.Length != 0)
{
foreach (string tag in column.Metadata.Tags)
tags.Add(tag);
}
if (column.SubTasks is not null && column.SubTasks.Length != 0)
{
foreach (SubTask subTask in column.SubTasks)
{
subTasks += 1;
if (subTask.Completed)
completed += 1;
}
}
subtasks = subTasks == 0 ? subtasks = null : $"{completed} / {subTasks}";
row.Add(new(column.Name, new(tags), subtasks));
}
}
if (!check)
break;
if (results.Count > 0)
{
if (results[0].Count != row.Count)
throw new Exception("Rows must match!");
}
results.Add(new(row));
}
return new(results);
}
private static void WriteFile(string destinationFile, Heading[] headings, ReadOnlyCollection<ReadOnlyCollection<Record>> recordCollection)
{
string title;
string completed;
List<string> lines =
[
"<html>",
"<head>",
"<style>",
":root {",
"color-scheme: light dark;",
"}",
"body {",
"color: light-dark(#333b3c, #efedea);",
"background-color: light-dark(#efedea, #333b3c);",
"}",
"td {",
"vertical-align: top;",
"}",
".title {",
"font-weight: bold;",
"}",
".complete {",
"font-size: small;",
"}",
".speech-bubble {",
"position: relative;",
"background: darkCyan;",
"border-radius: .4em;",
"}",
".speech-bubble:after {",
"content: '';",
"position: absolute;",
"bottom: 0;",
"left: 50%;",
"width: 0;",
"height: 0;",
"border: 2px solid transparent;",
"border-top-color: #00aabb;",
"border-bottom: 0;",
"margin-left: -2px;",
"margin-bottom: -2px;",
"}",
"</style>",
"</head>",
"<table border=\"1\">",
"<tr>"
];
foreach (Heading heading in headings)
lines.Add($"<th>{heading.Name}</th>");
lines.Add("</tr>");
foreach (ReadOnlyCollection<Record> records in recordCollection)
{
lines.Add("<tr>");
foreach (Record record in records)
{
lines.Add("<td>");
title = record.Title is null ? "&nbsp;" : record.Title;
completed = record.Completed is null ? "&nbsp;" : record.Completed;
lines.Add($"<div class=\"title\">{title}</div>");
lines.Add("<div>");
foreach (string tag in record.Tags)
lines.Add($"<span class=\"speech-bubble\">{tag}</span>");
lines.Add("</div>");
lines.Add($"<div class=\"completed\">{completed}</div>");
lines.Add("</td>");
}
lines.Add("</tr>");
}
lines.Add("</table>");
lines.Add("</html>");
File.WriteAllLines(destinationFile, lines);
}
private static void ParseKanbn(ILogger<Worker> logger, string destinationFile, string json)
{
Root? root = JsonSerializer.Deserialize(json, Helper20240822RootSourceGenerationContext.Default.Root);
if (root is null)
logger.LogInformation("<{root}> is null!", root);
else if (root.Lanes.Length != 1)
logger.LogInformation("{root.Lanes} != 1", root.Lanes.Length);
else if (root.Lanes[0].Columns.Length != root.Headings.Length)
logger.LogInformation("{root[0].Columns.Lanes} != {root.Headings}", root.Lanes[0].Columns.Length, root.Headings.Length);
else
{
ReadOnlyCollection<ReadOnlyCollection<Record>> recordCollection = GetRecords(root.Lanes[0].Columns);
WriteFile(destinationFile, root.Headings, recordCollection);
}
}
internal static void ParseKanbn(ILogger<Worker> logger, List<string> args)
{
string sourceDirectory = Path.GetFullPath(args[0]);
string sourceFile = Path.Combine(sourceDirectory, args[2]);
string destinationFile = Path.Combine(sourceDirectory, $"{DateTime.Now.Ticks}-{args[3]}");
if (!File.Exists(sourceFile))
logger.LogInformation("<{sourceFile}> doesn't exist!", sourceFile);
else
{
string json = File.ReadAllText(sourceFile);
ParseKanbn(logger, destinationFile, json);
}
}
}

View File

@ -0,0 +1,248 @@
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240828
{
public record HeaderCommon(DateTime Date,
string? Employee,
string? Layer,
string? MesEntity,
string? PSN,
string? Quantity,
string? RDS,
string? Reactor,
string? Recipe,
string? Zone);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(HeaderCommon))]
internal partial class HeaderCommonSourceGenerationContext : JsonSerializerContext
{
}
public record Record(string? CassetteId,
ReadOnlyCollection<string>? CassetteSegments,
DateTime? Date,
string? Employee,
ReadOnlyCollection<string>? EquipmentSegments,
int I,
string? LastDate,
ReadOnlyCollection<ReadOnlyCollection<string>>? Matches);
private static Record GetRecord(int i, string? lastDate, ReadOnlyCollection<ReadOnlyCollection<string>> matches)
{
Record result;
if (matches.Count != 4 || matches[0].Count != 3 || matches[3].Count != 3)
result = new Record(null, null, null, null, null, i, null, null);
else
{
string[] equipmentSegments = matches[1][2].Split('|');
if (equipmentSegments.Length != 2)
result = new Record(null, null, null, null, null, i, null, null);
else
{
string[] cassetteIdSegments = matches[3][2].Split('|');
if (cassetteIdSegments.Length <= 3)
result = new Record(null, null, null, null, null, i, null, null);
else
{
string cassetteId = Regex.Replace(cassetteIdSegments[2], @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]", "_").Split('\r')[0].Split('\n')[0];
result = new Record(cassetteId,
new(cassetteIdSegments),
DateTime.Parse(matches[0][0]),
matches[0][1],
new(equipmentSegments),
i,
lastDate,
new(matches));
}
}
}
return result;
}
private static ReadOnlyCollection<Record> GetRecords(string logDirectory, string logSearchPattern)
{
List<Record> results = [];
Record record;
string[] lines;
string[] logFiles = Directory.GetFiles(logDirectory, logSearchPattern, SearchOption.TopDirectoryOnly);
if (logFiles.Length > 0)
{ }
foreach (string logFile in new List<string>()) // logFiles)
{
lines = File.ReadAllLines(logFile);
for (int i = lines.Length - 1; i >= 0; i--)
{
record = GetRecord(lines, i);
i = record.I;
if (record.CassetteId is null || record.CassetteSegments is null || record.Date is null || record.Employee is null || record.EquipmentSegments is null || record.Matches is null)
{
if (i < 4)
break;
continue;
}
results.Add(record);
}
}
return new(results);
}
private static Dictionary<int, ReadOnlyCollection<Record>> GetKeyValuePairs(Dictionary<int, List<Record>> keyValuePairs)
{
Dictionary<int, ReadOnlyCollection<Record>> results = [];
foreach (KeyValuePair<int, List<Record>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, new(keyValuePair.Value));
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;
int totalMinutes;
TimeSpan timeSpan;
List<Record>? collection;
Dictionary<int, List<Record>> keyValuePairs = [];
ReadOnlyCollection<Record> records = GetRecords(logDirectory, logSearchPattern);
foreach (Record record in records)
{
if (record.CassetteId is null || record.CassetteSegments is null || record.Date is null || record.Employee is null || record.EquipmentSegments is null || record.Matches is null)
continue;
timeSpan = TimeSpan.FromTicks(record.Date.Value.Ticks);
totalMinutes = (int)Math.Floor(timeSpan.TotalMinutes);
if (!keyValuePairs.TryGetValue(totalMinutes, out collection))
{
keyValuePairs.Add(totalMinutes, []);
if (!keyValuePairs.TryGetValue(totalMinutes, out collection))
throw new Exception();
}
collection.Add(record);
}
results = GetKeyValuePairs(keyValuePairs);
return results;
}
internal static void MoveWaferCounterToArchive(ILogger<Worker> logger, List<string> args)
{
string json;
string keyFile;
string? recipe;
string[] lines;
string checkFile;
string directory;
string? quantity;
string runDataSheet;
string checkDirectory;
HeaderCommon headerCommon;
string logDateFormat = args[3];
string wcSearchPattern = args[5];
string logSearchPattern = $"SKIP---{args[2]}";
string logDirectory = Path.GetFullPath(args[0]);
string sourceDirectory = Path.GetFullPath(args[4]);
string archiveDirectory = Path.GetFullPath(args[6]);
FileInfo[] collection = Directory.GetFiles(sourceDirectory, wcSearchPattern, SearchOption.AllDirectories).Select(l => new FileInfo(l)).ToArray();
logger.LogInformation("Found {collection}(s)", collection.Length);
foreach (FileInfo fileInfo in collection)
{
if (fileInfo.DirectoryName is null || !fileInfo.DirectoryName.Contains('-'))
continue;
lines = File.ReadAllLines(fileInfo.FullName);
recipe = lines.Length < 2 ? null : lines[1];
quantity = lines.Length < 1 ? null : lines[0];
keyFile = $"{fileInfo.FullName}.txt";
if (!File.Exists(keyFile))
continue;
lines = File.ReadAllLines(keyFile);
if (lines.Length != 1)
continue;
runDataSheet = Regex.Replace(lines[0], @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]", ".").Split('\r')[0].Split('\n')[0];
directory = Path.Combine(fileInfo.DirectoryName, runDataSheet);
checkDirectory = Path.Combine(directory, fileInfo.LastWriteTime.Ticks.ToString());
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
checkFile = Path.Combine(directory, fileInfo.Name);
if (File.Exists(checkFile))
continue;
headerCommon = new(fileInfo.LastWriteTime, null, null, null, null, quantity, runDataSheet, null, recipe, null);
json = JsonSerializer.Serialize(headerCommon, HeaderCommonSourceGenerationContext.Default.HeaderCommon);
File.Move(fileInfo.FullName, checkFile);
File.Delete(keyFile);
checkFile = Path.Combine(checkDirectory, $"{fileInfo.Name}.json");
if (File.Exists(checkFile))
continue;
File.WriteAllText(checkFile, json);
Directory.SetLastWriteTime(checkDirectory, fileInfo.LastWriteTime);
}
Record record;
int totalMinutes;
string weekOfYear;
TimeSpan timeSpan;
ReadOnlyCollection<Record>? records;
Calendar calendar = new CultureInfo("en-US").Calendar;
Dictionary<int, ReadOnlyCollection<Record>> keyValuePairs = GetKeyValuePairs(logSearchPattern, logDirectory);
logger.LogInformation("Mapped {keyValuePairs}(s)", keyValuePairs.Count);
foreach (FileInfo fileInfo in collection)
{
if (fileInfo.DirectoryName is null || fileInfo.DirectoryName.Contains('-'))
continue;
timeSpan = TimeSpan.FromTicks(fileInfo.LastWriteTime.Ticks);
totalMinutes = (int)Math.Floor(timeSpan.TotalMinutes);
if (!keyValuePairs.TryGetValue(totalMinutes, out records))
continue;
if (records.Count != 1)
continue;
record = records[0];
if (record.CassetteId is null || record.CassetteSegments is null || record.Date is null || record.Employee is null || record.EquipmentSegments is null || record.Matches is null)
continue;
weekOfYear = $"{record.Date.Value.Year}_Week_{calendar.GetWeekOfYear(record.Date.Value, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}";
checkDirectory = Path.Combine(archiveDirectory,
string.Join('-', record.EquipmentSegments.Reverse()),
weekOfYear,
record.Date.Value.ToString("yyyy-MM-dd"),
record.CassetteId);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
checkFile = Path.Combine(checkDirectory, fileInfo.Name);
if (File.Exists(checkFile))
continue;
File.Move(fileInfo.FullName, checkFile);
lines = record.Matches.Select(l => string.Join(',', l)).ToArray();
File.WriteAllLines($"{checkFile}.txt", lines);
}
}
}

View File

@ -0,0 +1,236 @@
#if WorkItems
using File_Folder_Helper.ADO2024.PI3.WorkItems;
using Microsoft.Extensions.Logging;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using System.Collections.ObjectModel;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Web;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240830
{
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,
object[] 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);
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 = [];
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)
{
results.Add(workItem.Id);
if (results.Count > 199)
break;
}
foreach (int id in results)
_ = result.Append(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 targetFileLocation, string ids)
{
List<ValueWithReq> results = [];
string json;
string file;
Value? value;
JsonElement[] jsonElements;
Task<HttpResponseMessage> httpResponseMessageTask = httpClient.GetAsync(string.Concat(basePage, api, $"/workitems?ids={ids}&$expand=Relations"));
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;
if (value.Id == 120593)
GetSingle(httpClient, basePage, api, targetFileLocation, value.Id);
file = Path.Combine(targetFileLocation, $"{-1}-{value.Id}.json");
File.WriteAllText(file, json);
results.Add(new(value, -1, json));
}
}
return new(results);
}
private static void CompareWorkItems(WorkItemTrackingHttpClient workItemTrackingHttpClient,
string sourceDirectory,
string project,
string site,
ReadOnlyCollection<ValueWithReq> valueWithReqCollection)
{
ArgumentNullException.ThrowIfNull(workItemTrackingHttpClient);
if (string.IsNullOrEmpty(project))
throw new ArgumentException($"'{nameof(project)}' cannot be null or empty.", nameof(project));
if (string.IsNullOrEmpty(sourceDirectory))
throw new ArgumentException($"'{nameof(sourceDirectory)}' cannot be null or empty.", nameof(site));
if (string.IsNullOrEmpty(site))
throw new ArgumentException($"'{nameof(site)}' cannot be null or empty.", nameof(site));
ReadOnlyCollection<WorkItem> workItems = GetWorkItems(valueWithReqCollection);
string file = Path.Combine(sourceDirectory, $"_.json");
string json = JsonSerializer.Serialize(workItems);
File.WriteAllText(file, json);
foreach (WorkItem workItem in workItems)
{
if (workItem is null)
{ }
}
// https://stackoverflow.com/questions/18153998/how-do-i-remove-all-html-tags-from-a-string-without-knowing-which-tags-are-in-it
}
private static void CompareWorkItems(HttpClient httpClient,
string targetFileLocation,
string basePage,
string api,
string query,
WorkItemTrackingHttpClient workItemTrackingHttpClient,
string project,
string site)
{
string ids = GetIds(httpClient, basePage, api, query);
ReadOnlyCollection<ValueWithReq> valueWithReqCollection = string.IsNullOrEmpty(ids) ? new([]) : GetWorkItems(httpClient, basePage, api, targetFileLocation, ids);
CompareWorkItems(workItemTrackingHttpClient, targetFileLocation, project, site, valueWithReqCollection);
}
}
#endif

View File

@ -0,0 +1,65 @@
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Globalization;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240910
{
internal static void MoveFilesToWeekOfYear(ILogger<Worker> logger, List<string> args)
{
string day;
string year;
string yearB;
string yearC;
string checkFile;
FileInfo fileInfo;
string weekOfYear;
int weekOfYearValue;
string checkDirectory;
string searchPattern = args[2];
ReadOnlyCollection<string> directoryNames;
string sourceDirectory = Path.GetFullPath(args[0]);
Calendar calendar = new CultureInfo("en-US").Calendar;
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
logger.LogInformation("With search pattern '{SearchPattern}' found {files}", searchPattern, files.Length);
foreach (string file in files)
{
fileInfo = new(file);
if (string.IsNullOrEmpty(fileInfo.DirectoryName))
continue;
checkDirectory = string.Empty;
year = $"{fileInfo.LastWriteTime:yyyy}";
yearB = $"{fileInfo.LastWriteTime:yyyy}_Year";
day = fileInfo.LastWriteTime.ToString("yyyy-MM-dd");
directoryNames = Helpers.HelperDirectory.GetDirectoryNames(fileInfo.DirectoryName);
weekOfYearValue = calendar.GetWeekOfYear(fileInfo.LastWriteTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
yearC = weekOfYearValue < 27 ? $"{fileInfo.LastWriteTime:yyyy}_Year_A" : $"{fileInfo.LastWriteTime:yyyy}_Year_Z";
weekOfYear = $"{fileInfo.LastWriteTime.Year}_Week_{weekOfYearValue:00}";
foreach (string directoryName in directoryNames)
{
if (directoryName == year || directoryName == yearB || directoryName == yearC || directoryName == weekOfYear || directoryName == day)
continue;
checkDirectory = Path.Combine(checkDirectory, directoryName);
}
if (string.IsNullOrEmpty(checkDirectory))
continue;
checkDirectory = Path.Combine(checkDirectory, yearC, weekOfYear, day);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
checkFile = Path.Combine(checkDirectory, fileInfo.Name);
if (checkFile.Length > 256 || checkFile == fileInfo.FullName)
continue;
try
{
if (File.Exists(checkFile))
continue;
File.Move(fileInfo.FullName, checkFile);
}
catch (Exception ex)
{ logger.LogInformation(ex, $"Inner loop error <{fileInfo.FullName}>!"); }
}
Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory);
}
}

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

@ -0,0 +1,58 @@
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
namespace File_Folder_Helper.ADO2024.PI3;
internal static partial class Helper20240916
{
private static ReadOnlyCollection<string> Get(string searchPattern, string[] configFileLines)
{
List<string> results = [];
string[] segments;
foreach (string line in configFileLines)
{
if (line.Length < 2 || line[0] == '#')
continue;
segments = line.Split(searchPattern);
if (segments.Length < 2)
continue;
results.Add(segments[1].Split(';')[0]);
}
return results.AsReadOnly();
}
internal static void DebugProxyPass(ILogger<Worker> logger, List<string> args)
{
string debug;
string enable;
string[] lines;
string fileName;
string[] segments;
string configFile = args[3];
string searchPattern = args[2];
string searchPatternB = args[4];
string searchPatternC = args[5];
string sourceDirectory = Path.GetFullPath(args[0]);
string[] configFileLines = File.ReadAllLines(configFile);
ReadOnlyCollection<string> enabledSites = Get(searchPatternB, configFileLines);
logger.LogInformation("Found {files} file(s)", enabledSites.Count);
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
logger.LogInformation("With search pattern '{SearchPattern}' found {files} file(s)", searchPattern, files.Length);
foreach (string file in files)
{
debug = string.Empty;
lines = File.ReadAllLines(file);
fileName = Path.GetFileName(file);
foreach (string line in lines)
{
segments = line.Split(searchPatternC);
if (segments.Length < 2)
continue;
debug = segments[1].Trim();
}
enable = enabledSites.Contains(fileName) ? string.Empty : "# ";
logger.LogInformation("{enable}{searchPatternB}{fileName}; # https://{fileName}.phares.duckddns.org # {debug}", enable, searchPatternB, fileName, fileName, debug);
}
}
}

View File

@ -0,0 +1,65 @@
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 Helper20240925
{
public record Test(string Name,
long Value);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ReadOnlyCollection<Test>))]
internal partial class TestCollectionSourceGenerationContext : JsonSerializerContext
{
}
private static ReadOnlyCollection<Test> GetTests(string sourceDirectory, string searchPattern, string searchPatternB)
{
List<Test> results = [];
long test;
string[] lines;
string[] segments;
string[] segmentsB;
List<long> distinct = [];
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
foreach (string file in files)
{
lines = File.ReadAllLines(file);
foreach (string line in lines)
{
segments = line.Split(searchPatternB);
if (segments.Length < 2)
continue;
segmentsB = segments[1].Split(',');
if (segmentsB.Length < 2)
continue;
if (!long.TryParse(segmentsB[0], out test))
continue;
if (distinct.Contains(test))
continue;
distinct.Add(test);
results.Add(new(segmentsB[1].Trim('"'), test));
}
}
return (from l in results orderby l.Name.Length, l.Name select l).ToArray().AsReadOnly();
}
internal static void DistinctTests(ILogger<Worker> logger, List<string> args)
{
string searchPattern = args[2];
string searchPatternB = args[3];
string destinationDirectory = args[4];
string sourceDirectory = Path.GetFullPath(args[0]);
if (!Directory.Exists(destinationDirectory))
_ = Directory.CreateDirectory(destinationDirectory);
ReadOnlyCollection<Test> tests = GetTests(sourceDirectory, searchPattern, searchPatternB);
logger.LogInformation("Found {files} file(s)", tests.Count);
string json = JsonSerializer.Serialize(tests, TestCollectionSourceGenerationContext.Default.ReadOnlyCollectionTest);
string fileName = Path.Combine(destinationDirectory, ".json");
File.WriteAllText(fileName, json);
}
}

View File

@ -0,0 +1,177 @@
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 Helper20241002
{
public record Record(string? Calculation,
string Chart,
string Group,
string GroupId,
long Id,
string? RawCalculation,
string Test,
string TestId)
{
public static Record Get(Record record, string? calculation) =>
new(calculation,
record.Chart,
record.Group,
record.GroupId,
record.Id,
record.RawCalculation,
record.Test,
record.TestId);
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ReadOnlyCollection<Record>))]
internal partial class RecordCollectionSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ReadOnlyDictionary<string, Record>))]
internal partial class RecordDictionarySourceGenerationContext : JsonSerializerContext
{
}
private static string? GetCalculation(string searchPatternC, string[] lines, int i, string id, long idValue)
{
string? result = null;
string line;
long check;
string[] segments;
string[] segmentsB;
for (int j = i + 1; j < lines.Length; j++)
{
line = lines[j];
if (!line.Contains(id))
break;
segments = line.Split(searchPatternC);
if (segments.Length < 2)
continue;
segmentsB = segments[1].Split('=');
if (segmentsB.Length < 2)
continue;
if (!long.TryParse(segmentsB[0], out check))
continue;
if (check != idValue)
break;
result = segmentsB[1];
}
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 = [];
string id;
string line;
long idValue;
string[] lines;
string[] segments;
string[] segmentsB;
string[] segmentsC;
string? calculation;
string[] files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
foreach (string file in files)
{
lines = File.ReadAllLines(file);
for (int i = 0; i < lines.Length; i++)
{
line = lines[i];
segments = line.Split(searchPatternB);
if (segments.Length < 2)
continue;
segmentsB = segments[1].Split('=');
if (segmentsB.Length < 2)
continue;
if (!long.TryParse(segmentsB[0], out idValue))
continue;
id = segmentsB[0];
segmentsC = segments[1].Split(',');
if (segmentsC.Length < 4)
continue;
calculation = GetCalculation(searchPatternC, lines, i, id, idValue);
results.Add(new(null,
Path.GetFileName(file),
segmentsC[2].Trim('"'),
segmentsC[1],
idValue,
calculation,
segmentsC[4].Trim('"'),
segmentsC[3]));
}
}
return new(results);
}
private static ReadOnlyDictionary<string, Record> GetKeyValuePairs(ReadOnlyCollection<Record> records)
{
Dictionary<string, Record> results = [];
string key;
string? last = null;
foreach (Record record in records)
{
if (last is not null && record.Chart != last)
continue;
last = record.Chart;
key = GetKey(record);
if (results.ContainsKey(key))
continue;
results.Add(key, record);
}
return new(results);
}
internal static void ConvertInfinityQSProjectFiles(ILogger<Worker> logger, List<string> args)
{
string searchPattern = args[2];
string searchPatternB = args[3];
string searchPatternC = args[4];
string destinationDirectory = args[5];
string sourceDirectory = Path.GetFullPath(args[0]);
if (!Directory.Exists(destinationDirectory))
_ = Directory.CreateDirectory(destinationDirectory);
ReadOnlyCollection<Record> records = GetRecords(sourceDirectory, searchPattern, searchPatternB, searchPatternC);
logger.LogInformation("Found {records} records(s)", records.Count);
ReadOnlyDictionary<string, Record> collection = GetKeyValuePairs(records);
logger.LogInformation("Found {collection} collection(s)", collection.Count);
ReadOnlyDictionary<string, Record> keyValuePairs = GetKeyValuePairs(collection);
logger.LogInformation("Found {keyValuePairs}", keyValuePairs.Count);
string json = JsonSerializer.Serialize(keyValuePairs, RecordDictionarySourceGenerationContext.Default.ReadOnlyDictionaryStringRecord);
string fileName = Path.Combine(destinationDirectory, ".json");
File.WriteAllText(fileName, json);
}
}

View File

@ -0,0 +1,24 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WIQL;
public class Column
{
[JsonConstructor]
public Column(
string referenceName,
string name,
string url
)
{
ReferenceName = referenceName;
Name = name;
Url = url;
}
public string ReferenceName { get; set; } // { init; get; }
public string Name { get; set; } // { init; get; }
public string Url { get; set; } // { init; get; }
}
#endif

24
ADO2024/PI3/WIQL/Field.cs Normal file
View File

@ -0,0 +1,24 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WIQL;
public class Field
{
[JsonConstructor]
public Field(
string referenceName,
string name,
string url
)
{
ReferenceName = referenceName;
Name = name;
Url = url;
}
public string ReferenceName { get; set; } // { init; get; }
public string Name { get; set; } // { init; get; }
public string Url { get; set; } // { init; get; }
}
#endif

39
ADO2024/PI3/WIQL/Root.cs Normal file
View File

@ -0,0 +1,39 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WIQL;
public class Root
{
[JsonConstructor]
public Root(
string queryType,
string queryResultType,
DateTime asOf,
Column[] columns,
SortColumn[] sortColumns,
WorkItem[] workItems
)
{
QueryType = queryType;
QueryResultType = queryResultType;
AsOf = asOf;
Columns = columns;
SortColumns = sortColumns;
WorkItems = workItems;
}
public string QueryType { get; set; } // { init; get; }
public string QueryResultType { get; set; } // { init; get; }
public DateTime AsOf { get; set; } // { init; get; }
public Column[] Columns { get; set; } // { init; get; }
public SortColumn[] SortColumns { get; set; } // { init; get; }
public WorkItem[] WorkItems { get; set; } // { init; get; }
}
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNameCaseInsensitive = true)]
[JsonSerializable(typeof(Root))]
internal partial class WIQLRootSourceGenerationContext : JsonSerializerContext
{
}
#endif

View File

@ -0,0 +1,21 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WIQL;
public class SortColumn
{
[JsonConstructor]
public SortColumn(
Field field,
bool descending
)
{
Field = field;
Descending = descending;
}
public Field Field { get; set; } // { init; get; }
public bool Descending { get; set; } // { init; get; }
}
#endif

View File

@ -0,0 +1,21 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WIQL;
public class WorkItem
{
[JsonConstructor]
public WorkItem(
int id,
string url
)
{
Id = id;
Url = url;
}
public int Id { get; set; } // { init; get; }
public string Url { get; set; } // { init; get; }
}
#endif

View File

@ -0,0 +1,15 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class Avatar
{
[JsonConstructor]
public Avatar(
string href
) => Href = href;
public string Href { get; } // { init; get; }
}
#endif

View File

@ -0,0 +1,29 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class CommentVersionRef
{
[JsonConstructor]
public CommentVersionRef(
int commentId,
int version,
string url
)
{
CommentId = commentId;
Version = version;
URL = url;
}
[JsonPropertyName("commentId")]
public int CommentId { get; } // { init; get; }
[JsonPropertyName("version")]
public int Version { get; } // { init; get; }
[JsonPropertyName("url")]
public string URL { get; } // { init; get; }
}
#endif

View File

@ -0,0 +1,36 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class CustomRequester
{
[JsonConstructor]
public CustomRequester(
string descriptor,
string displayName,
string id,
string imageUrl,
Links links,
string uniqueName,
string url
)
{
Descriptor = descriptor;
DisplayName = displayName;
Id = id;
ImageUrl = imageUrl;
Links = links;
UniqueName = uniqueName;
Url = url;
}
[JsonPropertyName("descriptor")] public string Descriptor { get; }
[JsonPropertyName("displayName")] public string DisplayName { get; }
[JsonPropertyName("id")] public string Id { get; }
[JsonPropertyName("imageUrl")] public string ImageUrl { get; }
[JsonPropertyName("_links")] public Links Links { get; }
[JsonPropertyName("uniqueName")] public string UniqueName { get; }
[JsonPropertyName("url")] public string Url { get; }
}
#endif

View File

@ -0,0 +1,101 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class Fields
{
[JsonConstructor]
public Fields(int customRRminusOE,
CustomRequester? customRequester,
float customWSJF,
int microsoftVSTSCommonBusinessValue,
DateTime microsoftVSTSCommonClosedDate,
int microsoftVSTSCommonPriority,
DateTime microsoftVSTSCommonResolvedDate,
DateTime microsoftVSTSCommonStateChangeDate,
float microsoftVSTSCommonTimeCriticality,
float? microsoftVSTSSchedulingEffort,
DateTime microsoftVSTSSchedulingStartDate,
DateTime microsoftVSTSSchedulingTargetDate,
string systemAreaPath,
SystemAssignedTo systemAssignedTo,
SystemChangedBy systemChangedBy,
DateTime systemChangedDate,
int systemCommentCount,
SystemCreatedBy systemCreatedBy,
DateTime systemCreatedDate,
string systemDescription,
string systemHistory,
string systemIterationPath,
int systemParent,
string systemReason,
string systemState,
string systemTags,
string systemTeamProject,
string systemTitle,
string systemWorkItemType)
{
CustomRequester = customRequester;
CustomRRminusOE = customRRminusOE;
CustomWSJF = customWSJF;
MicrosoftVSTSCommonBusinessValue = microsoftVSTSCommonBusinessValue;
MicrosoftVSTSCommonClosedDate = microsoftVSTSCommonClosedDate;
MicrosoftVSTSCommonPriority = microsoftVSTSCommonPriority;
MicrosoftVSTSCommonResolvedDate = microsoftVSTSCommonResolvedDate;
MicrosoftVSTSCommonStateChangeDate = microsoftVSTSCommonStateChangeDate;
MicrosoftVSTSCommonTimeCriticality = microsoftVSTSCommonTimeCriticality;
MicrosoftVSTSSchedulingEffort = microsoftVSTSSchedulingEffort;
MicrosoftVSTSSchedulingStartDate = microsoftVSTSSchedulingStartDate;
MicrosoftVSTSSchedulingTargetDate = microsoftVSTSSchedulingTargetDate;
SystemAreaPath = systemAreaPath;
SystemAssignedTo = systemAssignedTo;
SystemChangedBy = systemChangedBy;
SystemChangedDate = systemChangedDate;
SystemCommentCount = systemCommentCount;
SystemCreatedBy = systemCreatedBy;
SystemCreatedDate = systemCreatedDate;
SystemDescription = systemDescription;
SystemHistory = systemHistory;
SystemIterationPath = systemIterationPath;
SystemParent = systemParent;
SystemReason = systemReason;
SystemState = systemState;
SystemTags = systemTags;
SystemTeamProject = systemTeamProject;
SystemTitle = systemTitle;
SystemWorkItemType = systemWorkItemType;
}
[JsonPropertyName("Custom.Requester")] public CustomRequester? CustomRequester { get; } // { init; get; }
[JsonPropertyName("Custom.RRminusOE")] public int CustomRRminusOE { get; } // { init; get; }
[JsonPropertyName("Custom.WSJF")] public float CustomWSJF { get; } // { init; get; }
[JsonPropertyName("Microsoft.VSTS.Common.BusinessValue")] public int MicrosoftVSTSCommonBusinessValue { get; } // { init; get; }
[JsonPropertyName("Microsoft.VSTS.Common.ClosedDate")] public DateTime MicrosoftVSTSCommonClosedDate { get; } // { init; get; }
[JsonPropertyName("Microsoft.VSTS.Common.Priority")] public int MicrosoftVSTSCommonPriority { get; } // { init; get; }
[JsonPropertyName("Microsoft.VSTS.Common.ResolvedDate")] public DateTime MicrosoftVSTSCommonResolvedDate { get; } // { init; get; }
[JsonPropertyName("Microsoft.VSTS.Common.StateChangeDate")] public DateTime MicrosoftVSTSCommonStateChangeDate { get; } // { init; get; }
[JsonPropertyName("Microsoft.VSTS.Common.TimeCriticality")] public float MicrosoftVSTSCommonTimeCriticality { get; } // { init; get; }
[JsonPropertyName("Microsoft.VSTS.Scheduling.Effort")] public float? MicrosoftVSTSSchedulingEffort { get; } // { init; get; }
[JsonPropertyName("Microsoft.VSTS.Scheduling.StartDate")] public DateTime MicrosoftVSTSSchedulingStartDate { get; } // { init; get; }
[JsonPropertyName("Microsoft.VSTS.Scheduling.TargetDate")] public DateTime MicrosoftVSTSSchedulingTargetDate { get; } // { init; get; }
[JsonPropertyName("System.AreaPath")] public string SystemAreaPath { get; } // { init; get; }
[JsonPropertyName("System.AssignedTo")] public SystemAssignedTo? SystemAssignedTo { get; } // { init; get; }
[JsonPropertyName("System.ChangedBy")] public SystemChangedBy SystemChangedBy { get; } // { init; get; }
[JsonPropertyName("System.ChangedDate")] public DateTime SystemChangedDate { get; } // { init; get; }
[JsonPropertyName("System.CommentCount")] public int SystemCommentCount { get; } // { init; get; }
[JsonPropertyName("System.CreatedBy")] public SystemCreatedBy SystemCreatedBy { get; } // { init; get; }
[JsonPropertyName("System.CreatedDate")] public DateTime SystemCreatedDate { get; } // { init; get; }
[JsonPropertyName("System.Description")] public string SystemDescription { get; } // { init; get; }
[JsonPropertyName("System.History")] public string SystemHistory { get; } // { init; get; }
[JsonPropertyName("System.IterationPath")] public string SystemIterationPath { get; } // { init; get; }
[JsonPropertyName("System.Parent")] public int SystemParent { get; } // { init; get; }
[JsonPropertyName("System.Reason")] public string SystemReason { get; } // { init; get; }
[JsonPropertyName("System.State")] public string SystemState { get; } // { init; get; }
[JsonPropertyName("System.Tags")] public string SystemTags { get; } // { init; get; }
[JsonPropertyName("System.TeamProject")] public string SystemTeamProject { get; } // { init; get; }
[JsonPropertyName("System.Title")] public string SystemTitle { get; } // { init; get; }
[JsonPropertyName("System.WorkItemType")] public string SystemWorkItemType { get; } // { init; get; }
}
#endif

View File

@ -0,0 +1,15 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class Html
{
[JsonConstructor]
public Html(
string href
) => Href = href;
public string Href { get; } // { init; get; }
}
#endif

View File

@ -0,0 +1,16 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class Links
{
[JsonConstructor]
public Links(
Avatar avatar
) => Avatar = avatar;
[JsonPropertyName("avatar")]
public Avatar Avatar { get; }
}
#endif

View File

@ -0,0 +1,49 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class SystemAssignedTo
{
[JsonConstructor]
public SystemAssignedTo(
string displayName,
string url,
Links links,
string id,
string uniqueName,
string imageUrl,
string descriptor
)
{
DisplayName = displayName;
Url = url;
Links = links;
Id = id;
UniqueName = uniqueName;
ImageUrl = imageUrl;
Descriptor = descriptor;
}
[JsonPropertyName("displayName")]
public string DisplayName { get; }
[JsonPropertyName("url")]
public string Url { get; }
[JsonPropertyName("_links")]
public Links Links { get; }
[JsonPropertyName("id")]
public string Id { get; }
[JsonPropertyName("uniqueName")]
public string UniqueName { get; }
[JsonPropertyName("imageUrl")]
public string ImageUrl { get; }
[JsonPropertyName("descriptor")]
public string Descriptor { get; }
}
#endif

View File

@ -0,0 +1,49 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class SystemChangedBy
{
[JsonConstructor]
public SystemChangedBy(
string displayName,
string url,
Links links,
string id,
string uniqueName,
string imageUrl,
string descriptor
)
{
DisplayName = displayName;
Url = url;
Links = links;
Id = id;
UniqueName = uniqueName;
ImageUrl = imageUrl;
Descriptor = descriptor;
}
[JsonPropertyName("displayName")]
public string DisplayName { get; }
[JsonPropertyName("url")]
public string Url { get; }
[JsonPropertyName("_links")]
public Links Links { get; }
[JsonPropertyName("id")]
public string Id { get; }
[JsonPropertyName("uniqueName")]
public string UniqueName { get; }
[JsonPropertyName("imageUrl")]
public string ImageUrl { get; }
[JsonPropertyName("descriptor")]
public string Descriptor { get; }
}
#endif

View File

@ -0,0 +1,49 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class SystemCreatedBy
{
[JsonConstructor]
public SystemCreatedBy(
string displayName,
string url,
Links links,
string id,
string uniqueName,
string imageUrl,
string descriptor
)
{
DisplayName = displayName;
Url = url;
Links = links;
Id = id;
UniqueName = uniqueName;
ImageUrl = imageUrl;
Descriptor = descriptor;
}
[JsonPropertyName("displayName")]
public string DisplayName { get; }
[JsonPropertyName("url")]
public string Url { get; }
[JsonPropertyName("_links")]
public Links Links { get; }
[JsonPropertyName("id")]
public string Id { get; }
[JsonPropertyName("uniqueName")]
public string UniqueName { get; }
[JsonPropertyName("imageUrl")]
public string ImageUrl { get; }
[JsonPropertyName("descriptor")]
public string Descriptor { get; }
}
#endif

View File

@ -0,0 +1,56 @@
#if WorkItems
using System.Text.Json.Serialization;
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class Value
{
[JsonConstructor]
public Value(
int id,
int rev,
Fields fields,
object[] relations,
CommentVersionRef commentVersionRef,
string url
)
{
Id = id;
Rev = rev;
Fields = fields;
Relations = relations;
CommentVersionRef = commentVersionRef;
Url = url;
}
[JsonPropertyName("id")]
public int Id { get; }
[JsonPropertyName("rev")]
public int Rev { get; }
[JsonPropertyName("fields")]
public Fields Fields { get; }
[JsonPropertyName("relations")]
public object[] Relations { get; }
[JsonPropertyName("commentVersionRef")]
public CommentVersionRef CommentVersionRef { get; }
[JsonPropertyName("url")]
public string Url { get; }
}
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNameCaseInsensitive = true)]
[JsonSerializable(typeof(Value[]))]
internal partial class ValueCollectionSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNameCaseInsensitive = true)]
[JsonSerializable(typeof(Value))]
internal partial class ValueSourceGenerationContext : JsonSerializerContext
{
}
#endif

View File

@ -0,0 +1,21 @@
#if WorkItems
namespace File_Folder_Helper.ADO2024.PI3.WorkItems;
public class ValueWithReq
{
public ValueWithReq(
Value value,
int req,
string json
)
{
Value = value;
Req = req;
Json = json;
}
public Value Value { get; set; } // { init; get; }
public int Req { get; set; } // { init; get; }
public string Json { get; set; } // { init; get; }
}
#endif