Update Subtasks In Markdown Files
Better ISO support Only reviewing Files when comparing Extracted sections from UpdateSubTasksInMarkdownFiles
This commit is contained in:
parent
2361796bbf
commit
fb9289a572
33
.vscode/launch.json
vendored
33
.vscode/launch.json
vendored
@ -13,17 +13,42 @@
|
||||
"args": [
|
||||
"s",
|
||||
"X",
|
||||
"D:/5-Other-Small/Kanban/DEP08CEPIEPSILON",
|
||||
"D:/6-Other-Large-Z/https-linux-ubuntu-server/etc",
|
||||
"Day-Helper-2024-12-24",
|
||||
"dorico.phares.duckdns.org",
|
||||
"php",
|
||||
"444",
|
||||
"555",
|
||||
"666",
|
||||
"777",
|
||||
"888",
|
||||
"999",
|
||||
"s",
|
||||
"X",
|
||||
"D:/5-Other-Small/Kanban/Year-Season",
|
||||
"Day-Helper-2024-06-23",
|
||||
"*.md",
|
||||
"## Sub-tasks",
|
||||
"##_Sub-tasks",
|
||||
"code-insiders",
|
||||
"index.md",
|
||||
"- [,](",
|
||||
"## Done",
|
||||
"-_[,](",
|
||||
"##_Done",
|
||||
".kan",
|
||||
"D:/5-Other-Small/Kanban/Year-Season",
|
||||
"316940400000",
|
||||
"s",
|
||||
"X",
|
||||
"L:/",
|
||||
"Day-Helper-2024-12-17",
|
||||
"job.json",
|
||||
"333",
|
||||
"444",
|
||||
"555",
|
||||
"666",
|
||||
"777",
|
||||
"888",
|
||||
"999",
|
||||
"X",
|
||||
"L:/Git/Linux-Ubuntu-Server/etc/nginx/include",
|
||||
"Day-Helper-2024-09-16",
|
||||
"*.conf",
|
||||
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -27,6 +27,7 @@
|
||||
"Kanban",
|
||||
"kanbn",
|
||||
"Kofax",
|
||||
"mesfs",
|
||||
"NpgSql",
|
||||
"NSFX",
|
||||
"OBJE",
|
||||
@ -34,6 +35,7 @@
|
||||
"Permyriad",
|
||||
"pged",
|
||||
"Phares",
|
||||
"Renci",
|
||||
"Reparse",
|
||||
"Rijndael",
|
||||
"Serilog",
|
||||
|
@ -1,31 +1,96 @@
|
||||
using File_Folder_Helper.Helpers;
|
||||
using File_Folder_Helper.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace File_Folder_Helper.ADO2024.PI2;
|
||||
|
||||
internal static partial class Helper20240623
|
||||
{
|
||||
|
||||
private record SubTaskLine(string Text, bool Done, long? Ticks, int? Line);
|
||||
private record Record(int? CodeInsidersLine, string File, string[] Lines, int? StopLine, int? SubTasksLine);
|
||||
[GeneratedRegex("([A-Z]+(.))")]
|
||||
private static partial Regex UpperCase();
|
||||
|
||||
private static List<Record> GetRecords(string sourceDirectory, string searchPattern, string codeInsiders, string subTasks, string directoryFilter)
|
||||
[GeneratedRegex("[\\s!?.,@:;|\\\\/\"'`£$%\\^&*{}[\\]()<>~#+\\-=_¬]+")]
|
||||
private static partial Regex InvalidCharacter();
|
||||
|
||||
private record H1AndParamCase(string H1, string ParamCase);
|
||||
private record SubTaskLine(string Text, bool Done, long? Ticks, int? Line);
|
||||
private record Record(int? CodeInsidersLine, FileInfo FileInfo, LineNumber LineNumber, int? StopLine, int? SubTasksLine);
|
||||
|
||||
private record Input(long? AfterEpochTotalMilliseconds,
|
||||
string CodeInsiders,
|
||||
string? DestinationDirectory,
|
||||
string DirectoryFilter,
|
||||
string Done,
|
||||
string IndexFile,
|
||||
string SearchPattern,
|
||||
string SubTasks,
|
||||
string SourceDirectory,
|
||||
ReadOnlyCollection<string> Tasks);
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(Input))]
|
||||
private partial class InputSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
private static Record GetRecord(Input input, FileInfo fileInfo)
|
||||
{
|
||||
Record result;
|
||||
int? stopLine = null;
|
||||
int? subTasksLine = null;
|
||||
int? codeInsidersLine = null;
|
||||
LineNumber lineNumber = HelperMarkdown.GetLineNumbers(fileInfo);
|
||||
for (int i = 0; i < lineNumber.Lines.Count; i++)
|
||||
{
|
||||
if (lineNumber.Lines[i].StartsWith(input.CodeInsiders) && lineNumber.Lines[i][^1] == '"')
|
||||
{
|
||||
if (lineNumber.Lines.Count > i + 1 && lineNumber.Lines[i + 1] == "```")
|
||||
codeInsidersLine = i;
|
||||
}
|
||||
if (lineNumber.Lines[i] != input.SubTasks)
|
||||
continue;
|
||||
subTasksLine = i;
|
||||
if (codeInsidersLine is null)
|
||||
break;
|
||||
if (lineNumber.Lines.Count > i)
|
||||
{
|
||||
for (int j = i + 1; j < lineNumber.Lines.Count; j++)
|
||||
{
|
||||
if (lineNumber.Lines[j].Length > 0 && lineNumber.Lines[j][0] == '#')
|
||||
{
|
||||
stopLine = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stopLine ??= lineNumber.Lines.Count;
|
||||
break;
|
||||
}
|
||||
result = new(codeInsidersLine, fileInfo, lineNumber, stopLine, subTasksLine);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<Record> GetRecords(Input input)
|
||||
{
|
||||
List<Record> results = [];
|
||||
int? stopLine;
|
||||
string[] lines;
|
||||
int? subTasksLine;
|
||||
int? codeInsidersLine;
|
||||
ReadOnlyCollection<string> directoryNames = HelperDirectory.GetDirectoryNames(sourceDirectory);
|
||||
if (!directoryNames.Any(l => l.StartsWith(directoryFilter, StringComparison.CurrentCultureIgnoreCase)))
|
||||
Record record;
|
||||
FileInfo fileInfo;
|
||||
string sourceDirectory = input.SourceDirectory;
|
||||
ReadOnlyCollection<string> directoryNames = HelperDirectory.GetDirectoryNames(input.SourceDirectory);
|
||||
if (!directoryNames.Any(l => l.StartsWith(input.DirectoryFilter, StringComparison.CurrentCultureIgnoreCase)))
|
||||
{
|
||||
string directoryName;
|
||||
string[] checkDirectories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
string[] checkDirectories = Directory.GetDirectories(input.SourceDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (string checkDirectory in checkDirectories)
|
||||
{
|
||||
directoryName = Path.GetFileName(checkDirectory);
|
||||
if (directoryName.StartsWith(directoryFilter, StringComparison.CurrentCultureIgnoreCase))
|
||||
if (directoryName.StartsWith(input.DirectoryFilter, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
sourceDirectory = checkDirectory;
|
||||
break;
|
||||
@ -33,204 +98,506 @@ internal static partial class Helper20240623
|
||||
}
|
||||
}
|
||||
string[] subDirectories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
List<string> files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.TopDirectoryOnly).ToList();
|
||||
List<string> files = Directory.GetFiles(sourceDirectory, input.SearchPattern, SearchOption.TopDirectoryOnly).ToList();
|
||||
foreach (string subDirectory in subDirectories)
|
||||
files.AddRange(Directory.GetFiles(subDirectory, searchPattern, SearchOption.TopDirectoryOnly));
|
||||
files.AddRange(Directory.GetFiles(subDirectory, input.SearchPattern, SearchOption.TopDirectoryOnly));
|
||||
foreach (string file in files)
|
||||
{
|
||||
stopLine = null;
|
||||
subTasksLine = null;
|
||||
codeInsidersLine = null;
|
||||
lines = File.ReadAllLines(file);
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (lines[i].StartsWith(codeInsiders) && lines[i][^1] == '"')
|
||||
{
|
||||
if (lines.Length > i + 1 && lines[i + 1] == "```")
|
||||
codeInsidersLine = i;
|
||||
}
|
||||
if (lines[i] != subTasks)
|
||||
continue;
|
||||
subTasksLine = i;
|
||||
if (codeInsidersLine is null)
|
||||
break;
|
||||
if (lines.Length > i)
|
||||
{
|
||||
for (int j = i + 1; j < lines.Length; j++)
|
||||
{
|
||||
if (lines[j].Length > 0 && lines[j][0] == '#')
|
||||
{
|
||||
stopLine = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stopLine ??= lines.Length;
|
||||
break;
|
||||
}
|
||||
results.Add(new(codeInsidersLine, file, lines, stopLine, subTasksLine));
|
||||
fileInfo = new(file);
|
||||
record = GetRecord(input, fileInfo);
|
||||
results.Add(record);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<SubTaskLine> GetSubTasks(string subTasks, string[] tasks, bool? foundDone, string fallbackLine, FileInfo fileInfo)
|
||||
private static string GetParamCase(string value)
|
||||
{
|
||||
string result;
|
||||
StringBuilder stringBuilder = new(value);
|
||||
Match[] matches = UpperCase().Matches(value).ToArray();
|
||||
for (int i = matches.Length - 1; i > -1; i--)
|
||||
_ = stringBuilder.Insert(matches[i].Index, '-');
|
||||
string[] segments = InvalidCharacter().Split(stringBuilder.ToString().ToLower());
|
||||
result = string.Join('-', segments).Trim('-');
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<SubTaskLine> GetSubTaskLines(Input input, bool? foundDone, string fallbackLine, Record record)
|
||||
{
|
||||
List<SubTaskLine> results = [];
|
||||
char done;
|
||||
string line;
|
||||
string text;
|
||||
bool doneValue;
|
||||
string? h1 = null;
|
||||
SubTaskLine subTaskLine;
|
||||
bool foundSubTasks = false;
|
||||
int tasksZeroLength = tasks[0].Length;
|
||||
string[] lines = File.ReadAllLines(fileInfo.FullName);
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
int tasksZeroLength = input.Tasks[0].Length;
|
||||
long ticks = record.FileInfo.LastWriteTime.Ticks;
|
||||
for (int i = 0; i < record.LineNumber.Lines.Count; i++)
|
||||
{
|
||||
line = lines[i];
|
||||
if (line.StartsWith("# "))
|
||||
h1 = line[2..];
|
||||
if (!foundSubTasks && line == subTasks)
|
||||
line = record.LineNumber.Lines[i];
|
||||
if (!foundSubTasks && line == input.SubTasks)
|
||||
foundSubTasks = true;
|
||||
if (!foundSubTasks)
|
||||
continue;
|
||||
if (line.Length <= tasksZeroLength || !line.StartsWith(tasks[0]) || line[tasksZeroLength] is not ' ' and not 'x' || line[tasksZeroLength + 1] != ']')
|
||||
if (line.Length <= tasksZeroLength || !line.StartsWith(input.Tasks[0]) || line[tasksZeroLength] is not ' ' and not 'x' || line[tasksZeroLength + 1] != ']')
|
||||
continue;
|
||||
doneValue = foundDone is not null && foundDone.Value;
|
||||
subTaskLine = new($" {line}", doneValue, fileInfo.LastWriteTime.Ticks, i);
|
||||
subTaskLine = new($" {line}", doneValue, ticks, i);
|
||||
results.Add(subTaskLine);
|
||||
}
|
||||
doneValue = foundDone is not null && foundDone.Value;
|
||||
if (h1 is null)
|
||||
subTaskLine = new(fallbackLine, doneValue, fileInfo.LastWriteTime.Ticks, Line: null);
|
||||
if (record.LineNumber.H1 is null)
|
||||
subTaskLine = new(fallbackLine, doneValue, ticks, Line: null);
|
||||
else
|
||||
{
|
||||
fallbackLine = foundDone is null || !foundDone.Value ? $"- [ ] {fileInfo.LastWriteTime.Ticks} ~~~ {h1}" : $"- [x] {fileInfo.LastWriteTime.Ticks} ~~~ {h1}";
|
||||
subTaskLine = new(fallbackLine, doneValue, fileInfo.LastWriteTime.Ticks, Line: 0);
|
||||
done = foundDone is null || !foundDone.Value ? ' ' : 'x';
|
||||
string codeInsidersLine = record.CodeInsidersLine is null ? string.Empty : $" ~~{record.LineNumber.Lines[record.CodeInsidersLine.Value]}~~";
|
||||
text = $"- [{done}] {ticks} {record.LineNumber.Lines[record.LineNumber.H1.Value]}{codeInsidersLine}";
|
||||
subTaskLine = new(text, doneValue, ticks, Line: 0);
|
||||
}
|
||||
results.Add(subTaskLine);
|
||||
return new(results);
|
||||
}
|
||||
|
||||
internal static void UpdateSubTasksInMarkdownFiles(ILogger<Worker> logger, List<string> args)
|
||||
private static string GetSeasonName(int dayOfYear)
|
||||
{
|
||||
int lineCheck;
|
||||
string result = dayOfYear switch
|
||||
{
|
||||
< 78 => "0.Winter",
|
||||
< 124 => "1.Spring",
|
||||
< 171 => "2.Spring",
|
||||
< 217 => "3.Summer",
|
||||
< 264 => "4.Summer",
|
||||
< 309 => "5.Fall",
|
||||
< 354 => "6.Fall",
|
||||
_ => "7.Winter"
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string[] GetIndexLines(string h1, ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection) =>
|
||||
[
|
||||
"---",
|
||||
"startedColumns:",
|
||||
" - 'In Progress'",
|
||||
"completedColumns:",
|
||||
" - Done",
|
||||
"---",
|
||||
string.Empty,
|
||||
$"# {h1}",
|
||||
string.Empty,
|
||||
"## Backlog",
|
||||
string.Empty,
|
||||
string.Join(Environment.NewLine, h1ParamCaseCollection.Select(l => $"- [{l.ParamCase}](tasks/{l.ParamCase}.md)")),
|
||||
string.Empty,
|
||||
"## Todo",
|
||||
string.Empty,
|
||||
"## In Progress",
|
||||
string.Empty,
|
||||
"## Done",
|
||||
string.Empty
|
||||
];
|
||||
|
||||
private static string[] GetCascadingStyleSheetsLines() =>
|
||||
[
|
||||
".kanbn-column-done .kanbn-column-task-list {",
|
||||
" border-color: #198038;",
|
||||
"}",
|
||||
string.Empty,
|
||||
".kanbn-task-data-created {",
|
||||
" display: none;",
|
||||
"}",
|
||||
string.Empty,
|
||||
".kanbn-task-data-workload {",
|
||||
" display: none;",
|
||||
"}"
|
||||
];
|
||||
|
||||
private static string GetSettingsLines() =>
|
||||
/*lang=json,strict*/ """
|
||||
{
|
||||
"[markdown]": {
|
||||
"editor.wordWrap": "off"
|
||||
},
|
||||
"cSpell.words": [
|
||||
"kanbn"
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
private static string GetTasksLines(string directory) =>
|
||||
/*lang=json,strict*/ """
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "File-Folder-Helper AOT s X Day-Helper-2024-06-23",
|
||||
"type": "shell",
|
||||
"command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe",
|
||||
"args": [
|
||||
"s",
|
||||
"X",
|
||||
"{}",
|
||||
"Day-Helper-2024-06-23",
|
||||
"*.md",
|
||||
"##_Sub-tasks",
|
||||
"code-insiders",
|
||||
"index.md",
|
||||
"-_[,](",
|
||||
"##_Done",
|
||||
".kan",
|
||||
"D:/5-Other-Small/Kanban/Year-Season",
|
||||
"316940400000"
|
||||
],
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
""".Replace("{}", directory.Replace('\\', '/'));
|
||||
|
||||
private static void FileWriteAllText(string path, string contents)
|
||||
{
|
||||
// string checkJson = Regex.Replace(File.ReadAllText(path), @"\s+", " ", RegexOptions.Multiline);
|
||||
// if (Regex.Replace(singletonJson, @"\s+", " ", RegexOptions.Multiline) != checkJson)
|
||||
// File.WriteAllText(path, singletonJson);
|
||||
string old = !File.Exists(path) ? string.Empty : File.ReadAllText(path);
|
||||
if (old != contents)
|
||||
File.WriteAllText(path, contents);
|
||||
}
|
||||
|
||||
private static void FileWriteAllText(string path, string[] contents) =>
|
||||
FileWriteAllText(path, string.Join(Environment.NewLine, contents));
|
||||
|
||||
private static ReadOnlyCollection<H1AndParamCase> GetH1ParamCaseCollection(Input input, ReadOnlyCollection<string> lines)
|
||||
{
|
||||
List<H1AndParamCase> results = [];
|
||||
string h1;
|
||||
string line;
|
||||
string paramCase;
|
||||
bool foundSubTasks = false;
|
||||
H1AndParamCase h1AndParamCase;
|
||||
int tasksZeroLength = input.Tasks[0].Length;
|
||||
for (int i = 0; i < lines.Count; i++)
|
||||
{
|
||||
line = lines[i];
|
||||
if (!foundSubTasks && line == input.SubTasks)
|
||||
foundSubTasks = true;
|
||||
if (!foundSubTasks)
|
||||
continue;
|
||||
if (line.Length <= tasksZeroLength || !line.StartsWith(input.Tasks[0]) || line[tasksZeroLength] is not ' ' and not 'x' || line[tasksZeroLength + 1] != ']')
|
||||
continue;
|
||||
h1 = line[(tasksZeroLength + 3)..];
|
||||
if (string.IsNullOrEmpty(h1))
|
||||
continue;
|
||||
paramCase = GetParamCase(h1);
|
||||
h1AndParamCase = new(h1, paramCase);
|
||||
results.Add(h1AndParamCase);
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
private static void CreateFiles(string directory, ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection)
|
||||
{
|
||||
foreach (H1AndParamCase h1ParamCase in h1ParamCaseCollection)
|
||||
FileWriteAllText(Path.Combine(directory, $"{h1ParamCase.ParamCase}.md"), $"# {h1ParamCase.H1}");
|
||||
}
|
||||
|
||||
private static string WriteAndGetIndexFile(string destinationDirectory, string h1, long verified, ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection)
|
||||
{
|
||||
string result;
|
||||
string[] indexLines = GetIndexLines(h1, h1ParamCaseCollection);
|
||||
DateTime utcEpochDateTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
DateTime dateTime = utcEpochDateTime.AddMilliseconds(verified).ToLocalTime();
|
||||
string seasonName = GetSeasonName(dateTime.DayOfYear);
|
||||
string verifiedDirectory = Path.Combine(destinationDirectory, $"{dateTime.Year}", $"{dateTime.Year}-{seasonName}", verified.ToString());
|
||||
string kanbanDirectory = Path.Combine(verifiedDirectory, ".kanbn");
|
||||
string tasksKanbanDirectory = Path.Combine(kanbanDirectory, "tasks");
|
||||
if (!Directory.Exists(tasksKanbanDirectory))
|
||||
_ = Directory.CreateDirectory(tasksKanbanDirectory);
|
||||
string verifiedVisualStudioCodeDirectory = Path.Combine(verifiedDirectory, ".vscode");
|
||||
if (!Directory.Exists(verifiedVisualStudioCodeDirectory))
|
||||
_ = Directory.CreateDirectory(verifiedVisualStudioCodeDirectory);
|
||||
result = Path.Combine(kanbanDirectory, "index.md");
|
||||
CreateFiles(tasksKanbanDirectory, h1ParamCaseCollection);
|
||||
FileWriteAllText(result, indexLines);
|
||||
FileWriteAllText(Path.Combine(kanbanDirectory, "board.css"), GetCascadingStyleSheetsLines());
|
||||
FileWriteAllText(Path.Combine(verifiedVisualStudioCodeDirectory, "settings.json"), GetSettingsLines());
|
||||
FileWriteAllText(Path.Combine(verifiedVisualStudioCodeDirectory, "tasks.json"), GetTasksLines(verifiedDirectory));
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<SubTaskLine> GetSubTaskLines(Input input, FileInfo fileInfo, LineNumber lineNumber)
|
||||
{
|
||||
List<SubTaskLine> results = [];
|
||||
char done;
|
||||
FileInfo f;
|
||||
Record record;
|
||||
bool doneValue;
|
||||
bool? foundDone;
|
||||
FileInfo fileInfo;
|
||||
string[] newLines;
|
||||
string[] segments;
|
||||
List<string> lines;
|
||||
string fallbackLine;
|
||||
string[] indexLines;
|
||||
string checkDirectory;
|
||||
string done = args[7];
|
||||
List<string> indexFiles;
|
||||
SubTaskLine subTaskLine;
|
||||
string subTasks = args[3];
|
||||
List<string> oldLines = [];
|
||||
bool? foundDone = null;
|
||||
ReadOnlyCollection<SubTaskLine> subTaskLines;
|
||||
for (int i = 0; i < lineNumber.Lines.Count; i++)
|
||||
{
|
||||
if (lineNumber.Lines[i] == input.Done)
|
||||
foundDone = true;
|
||||
segments = lineNumber.Lines[i].Split(input.Tasks[1]);
|
||||
doneValue = foundDone is not null && foundDone.Value;
|
||||
if (segments.Length > 2 || !segments[0].StartsWith(input.Tasks[0]))
|
||||
continue;
|
||||
done = foundDone is null || !foundDone.Value ? ' ' : 'x';
|
||||
fallbackLine = $"- [{done}] {segments[0][input.Tasks[0].Length..]} ~~FallbackLine~~";
|
||||
if (string.IsNullOrEmpty(fileInfo.DirectoryName))
|
||||
continue;
|
||||
f = new(Path.GetFullPath(Path.Combine(fileInfo.DirectoryName, segments[1][..^1])));
|
||||
if (!f.Exists)
|
||||
{
|
||||
results.Add(new(fallbackLine, doneValue, Ticks: null, Line: null));
|
||||
continue;
|
||||
}
|
||||
record = GetRecord(input, f);
|
||||
if (lineNumber.H1 is not null && record.LineNumber.H1 is not null)
|
||||
{
|
||||
string a = lineNumber.Lines[lineNumber.H1.Value];
|
||||
string b = record.LineNumber.Lines[record.LineNumber.H1.Value];
|
||||
if (b != a)
|
||||
{
|
||||
if (b != a)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
subTaskLines = GetSubTaskLines(input, doneValue, fallbackLine, record);
|
||||
for (int j = subTaskLines.Count - 1; j >= 0; j--)
|
||||
results.Add(subTaskLines[j]);
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
private static Input GetInput(List<string> args)
|
||||
{
|
||||
string indexFile = args[5];
|
||||
string searchPattern = args[2];
|
||||
string directoryFilter = args[8];
|
||||
string[] tasks = args[6].Split(',');
|
||||
string codeInsiders = $"{args[4]} \"";
|
||||
List<SubTaskLine> allSubTaskLines = [];
|
||||
ReadOnlyCollection<SubTaskLine> subTaskLines;
|
||||
string done = args[7].Replace('_', ' ');
|
||||
string subTasks = args[3].Replace('_', ' ');
|
||||
string sourceDirectory = Path.GetFullPath(args[0]);
|
||||
List<Record> records = GetRecords(sourceDirectory, searchPattern, codeInsiders, subTasks, directoryFilter);
|
||||
foreach (Record record in from l in records orderby l.SubTasksLine is null, l.CodeInsidersLine is null select l)
|
||||
string? destinationDirectory = args.Count < 8 ? null : Path.GetFullPath(args[9]);
|
||||
long? afterEpochTotalMilliseconds = args.Count < 9 ? null : long.Parse(args[10]);
|
||||
ReadOnlyCollection<string> tasks = args[6].Split(',').Select(l => l.Replace('_', ' ')).ToArray().AsReadOnly();
|
||||
Input input = new(afterEpochTotalMilliseconds, codeInsiders, destinationDirectory, directoryFilter, done, indexFile, searchPattern, subTasks, sourceDirectory, tasks);
|
||||
if (input.Tasks[0] != "- [" || input.Tasks[1] != "](")
|
||||
throw new Exception(JsonSerializer.Serialize(input, InputSourceGenerationContext.Default.Input));
|
||||
return input;
|
||||
}
|
||||
|
||||
private static string? MaybeWriteAndGetIndexFile(Input input, Record record, string? checkDirectory)
|
||||
{
|
||||
string? result;
|
||||
if (string.IsNullOrEmpty(checkDirectory) || input.AfterEpochTotalMilliseconds is null || string.IsNullOrEmpty(input.DestinationDirectory) || !checkDirectory.Contains(input.DestinationDirectory))
|
||||
result = null;
|
||||
else
|
||||
{
|
||||
if (record.SubTasksLine is null)
|
||||
continue;
|
||||
if (record.CodeInsidersLine is not null)
|
||||
logger.LogInformation("<{file}> has [{subTasks}]", Path.GetFileNameWithoutExtension(record.File), subTasks);
|
||||
if (record.LineNumber.H1 is null)
|
||||
result = null;
|
||||
else
|
||||
{
|
||||
logger.LogWarning("<{file}> has [{subTasks}] but doesn't have [{codeInsiders}]!", Path.GetFileNameWithoutExtension(record.File), subTasks, codeInsiders);
|
||||
continue;
|
||||
}
|
||||
if (record.StopLine is null)
|
||||
continue;
|
||||
checkDirectory = record.Lines[record.CodeInsidersLine.Value][codeInsiders.Length..^1];
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
{
|
||||
logger.LogError("<{checkDirectory}> doesn't exist", Path.GetFileName(checkDirectory));
|
||||
continue;
|
||||
}
|
||||
indexFiles = Directory.GetFiles(checkDirectory, indexFile, SearchOption.AllDirectories).ToList();
|
||||
if (indexFiles.Count != 1)
|
||||
{
|
||||
for (int i = indexFiles.Count - 1; i > -1; i--)
|
||||
string segment = Path.GetFileName(checkDirectory);
|
||||
DateTime utcEpochDateTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
long utcEpochTotalMilliseconds = (long)Math.Floor(DateTime.UtcNow.Subtract(utcEpochDateTime).TotalMilliseconds);
|
||||
if (!long.TryParse(segment, out long check) || check < input.AfterEpochTotalMilliseconds || check > utcEpochTotalMilliseconds)
|
||||
result = null;
|
||||
else
|
||||
{
|
||||
if (!indexFiles[i].Contains(directoryFilter, StringComparison.CurrentCultureIgnoreCase))
|
||||
indexFiles.RemoveAt(i);
|
||||
}
|
||||
if (indexFiles.Count != 1)
|
||||
{
|
||||
logger.LogError("<{checkDirectory}> doesn't have a [{indexFile}]", Path.GetFileName(checkDirectory), indexFile);
|
||||
continue;
|
||||
ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection = GetH1ParamCaseCollection(input, record.LineNumber.Lines);
|
||||
if (h1ParamCaseCollection.Count == 0)
|
||||
result = null;
|
||||
else
|
||||
result = WriteAndGetIndexFile(input.DestinationDirectory, record.LineNumber.Lines[record.LineNumber.H1.Value], check, h1ParamCaseCollection);
|
||||
}
|
||||
}
|
||||
foundDone = null;
|
||||
oldLines.Clear();
|
||||
allSubTaskLines.Clear();
|
||||
indexLines = File.ReadAllLines(indexFiles[0]);
|
||||
checkDirectory = Path.GetDirectoryName(indexFiles[0]) ?? throw new Exception();
|
||||
for (int i = 0; i < indexLines.Length; i++)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool FileWrite(Record record, List<string> newLines, double percent)
|
||||
{
|
||||
bool result = false;
|
||||
if (record.StopLine is not null && record.SubTasksLine is not null)
|
||||
{
|
||||
string contents;
|
||||
string progressLine;
|
||||
List<string> resultLines;
|
||||
resultLines = record.LineNumber.Lines.ToList();
|
||||
if (record.LineNumber.FrontMatterYamlEnd is not null)
|
||||
{
|
||||
if (indexLines[i] == done)
|
||||
foundDone = true;
|
||||
segments = indexLines[i].Split(tasks[1]);
|
||||
doneValue = foundDone is not null && foundDone.Value;
|
||||
if (segments.Length > 2 || !segments[0].StartsWith(tasks[0]))
|
||||
progressLine = $"progress: {percent}";
|
||||
if (record.LineNumber.Progress is not null)
|
||||
resultLines[record.LineNumber.Progress.Value] = progressLine;
|
||||
else
|
||||
{
|
||||
resultLines.Insert(record.LineNumber.FrontMatterYamlEnd.Value, progressLine);
|
||||
contents = string.Join(Environment.NewLine, resultLines);
|
||||
FileWriteAllText(record.FileInfo.FullName, contents);
|
||||
result = true;
|
||||
}
|
||||
if (!result && record.LineNumber.Completed is null && percent > 99.9)
|
||||
{
|
||||
resultLines.Insert(record.LineNumber.FrontMatterYamlEnd.Value, $"completed: {DateTime.Now:yyyy-MM-dd}");
|
||||
contents = string.Join(Environment.NewLine, resultLines);
|
||||
FileWriteAllText(record.FileInfo.FullName, contents);
|
||||
result = true;
|
||||
}
|
||||
if (!result && record.LineNumber.Completed is not null && percent < 99.9)
|
||||
{
|
||||
resultLines.RemoveAt(record.LineNumber.Completed.Value);
|
||||
contents = string.Join(Environment.NewLine, resultLines);
|
||||
FileWriteAllText(record.FileInfo.FullName, contents);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
if (!result)
|
||||
{
|
||||
for (int i = record.StopLine.Value - 1; i > record.SubTasksLine.Value + 1; i--)
|
||||
resultLines.RemoveAt(i);
|
||||
if (record.StopLine.Value == record.LineNumber.Lines.Count && resultLines[^1].Length == 0)
|
||||
resultLines.RemoveAt(resultLines.Count - 1);
|
||||
for (int i = 0; i < newLines.Count; i++)
|
||||
resultLines.Insert(record.SubTasksLine.Value + 1 + i, newLines[i]);
|
||||
resultLines.Insert(record.SubTasksLine.Value + 1, string.Empty);
|
||||
contents = string.Join(Environment.NewLine, resultLines);
|
||||
FileWriteAllText(record.FileInfo.FullName, contents);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static FileInfo GetIndexFileInfo(Input input, Record record, string codeInsidersLine)
|
||||
{
|
||||
FileInfo result;
|
||||
string? indexFile;
|
||||
List<string> results;
|
||||
string? checkDirectory = codeInsidersLine[input.CodeInsiders.Length..^1];
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(input.DestinationDirectory) && checkDirectory.Contains(input.DestinationDirectory))
|
||||
_ = Directory.CreateDirectory(checkDirectory);
|
||||
}
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
results = [];
|
||||
else
|
||||
{
|
||||
results = Directory.GetFiles(checkDirectory, input.IndexFile, SearchOption.AllDirectories).ToList();
|
||||
if (results.Count != 1)
|
||||
{
|
||||
for (int i = results.Count - 1; i > -1; i--)
|
||||
{
|
||||
if (!results[i].Contains(input.DirectoryFilter, StringComparison.CurrentCultureIgnoreCase))
|
||||
results.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
if (results.Count == 0)
|
||||
{
|
||||
indexFile = MaybeWriteAndGetIndexFile(input, record, checkDirectory);
|
||||
if (!string.IsNullOrEmpty(indexFile))
|
||||
results.Add(indexFile);
|
||||
}
|
||||
}
|
||||
result = results.Count == 0 ? new(Path.Combine(checkDirectory, input.IndexFile)) : new(results[0]);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static void UpdateSubTasksInMarkdownFiles(ILogger<Worker> logger, List<string> args)
|
||||
{
|
||||
bool reload;
|
||||
int allCount;
|
||||
int lineCheck;
|
||||
double percent;
|
||||
double doneCount;
|
||||
FileInfo fileInfo;
|
||||
List<Record> records;
|
||||
LineNumber lineNumber;
|
||||
List<string> newLines;
|
||||
bool reloadAny = false;
|
||||
string? checkDirectory;
|
||||
string codeInsidersLine;
|
||||
List<string> oldLines = [];
|
||||
Input input = GetInput(args);
|
||||
string fileNameWithoutExtension;
|
||||
ReadOnlyCollection<SubTaskLine> subTaskLines;
|
||||
for (int z = 0; z < 9; z++)
|
||||
{
|
||||
records = GetRecords(input);
|
||||
foreach (Record record in from l in records orderby l.SubTasksLine is null, l.CodeInsidersLine is null select l)
|
||||
{
|
||||
if (record.SubTasksLine is null)
|
||||
continue;
|
||||
fallbackLine = foundDone is null || !foundDone.Value ? $"- [ ] {segments[0][tasks[0].Length..]}" : $"- [x] {segments[0][tasks[0].Length..]}";
|
||||
fileInfo = new(Path.GetFullPath(Path.Combine(checkDirectory, segments[1][..^1])));
|
||||
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(record.FileInfo.FullName);
|
||||
if (record.CodeInsidersLine is not null)
|
||||
logger.LogInformation("<{file}> has [{subTasks}]", fileNameWithoutExtension, input.SubTasks);
|
||||
else
|
||||
{
|
||||
logger.LogWarning("<{file}> has [{subTasks}] but doesn't have [{codeInsiders}]!", fileNameWithoutExtension, input.SubTasks, input.CodeInsiders);
|
||||
continue;
|
||||
}
|
||||
if (record.StopLine is null)
|
||||
continue;
|
||||
codeInsidersLine = record.LineNumber.Lines[record.CodeInsidersLine.Value];
|
||||
fileInfo = GetIndexFileInfo(input, record, codeInsidersLine);
|
||||
if (!fileInfo.Exists)
|
||||
{
|
||||
allSubTaskLines.Add(new(fallbackLine, doneValue, Ticks: null, Line: null));
|
||||
checkDirectory = codeInsidersLine[input.CodeInsiders.Length..^1];
|
||||
logger.LogError("<{checkDirectory}> doesn't have a [{indexFile}]", Path.GetFileName(checkDirectory), input.IndexFile);
|
||||
continue;
|
||||
}
|
||||
subTaskLines = GetSubTasks(subTasks, tasks, doneValue, fallbackLine, fileInfo);
|
||||
if (subTaskLines.Count > 0)
|
||||
oldLines.Clear();
|
||||
checkDirectory = fileInfo.DirectoryName;
|
||||
lineNumber = HelperMarkdown.GetLineNumbers(fileInfo);
|
||||
subTaskLines = GetSubTaskLines(input, fileInfo, lineNumber);
|
||||
if (subTaskLines.Count == 0)
|
||||
continue;
|
||||
lineCheck = 0;
|
||||
for (int i = record.SubTasksLine.Value + 1; i < record.StopLine.Value - 1; i++)
|
||||
oldLines.Add(record.LineNumber.Lines[i]);
|
||||
if (subTaskLines.Any(l => l.Ticks is null))
|
||||
newLines = (from l in subTaskLines select l.Text).ToList();
|
||||
else
|
||||
newLines = (from l in subTaskLines orderby l.Done descending, l.Ticks, l.Line select l.Text).ToList();
|
||||
if (subTaskLines.Count == 0)
|
||||
percent = 0;
|
||||
else
|
||||
{
|
||||
subTaskLine = new($"", false, null, null);
|
||||
allSubTaskLines.Add(subTaskLine);
|
||||
for (int j = subTaskLines.Count - 1; j >= 0; j--)
|
||||
allSubTaskLines.Add(subTaskLines[j]);
|
||||
allCount = (from l in subTaskLines where l.Line is not null && l.Line.Value == 0 select 1).Count();
|
||||
doneCount = (from l in subTaskLines where l.Line is not null && l.Line.Value == 0 && l.Done select 1).Count();
|
||||
// done = allCount != doneCount ? ' ' : 'x';
|
||||
percent = allCount == 0 ? 0 : Math.Round(doneCount / allCount, 3);
|
||||
// newLines.Insert(0, $"- [{done}] Sub-tasks {doneCount} of {allCount} [{percent * 100}%]");
|
||||
}
|
||||
}
|
||||
if (allSubTaskLines.Count == 0)
|
||||
continue;
|
||||
lineCheck = 0;
|
||||
for (int i = record.SubTasksLine.Value + 1; i < record.StopLine.Value - 1; i++)
|
||||
oldLines.Add(record.Lines[i]);
|
||||
if (allSubTaskLines.Any(l => l.Ticks is null))
|
||||
newLines = (from l in allSubTaskLines select l.Text).ToArray();
|
||||
else
|
||||
newLines = (from l in allSubTaskLines orderby l.Done descending, l.Ticks, l.Line select l.Text).ToArray();
|
||||
if (newLines.Length == oldLines.Count)
|
||||
{
|
||||
for (int i = 0; i < newLines.Length; i++)
|
||||
if (newLines.Count == oldLines.Count)
|
||||
{
|
||||
if (newLines[i] != record.Lines[record.SubTasksLine.Value + 1 + i])
|
||||
for (int i = 0; i < newLines.Count; i++)
|
||||
{
|
||||
if (newLines[i] != record.LineNumber.Lines[record.SubTasksLine.Value + 1 + i])
|
||||
continue;
|
||||
lineCheck++;
|
||||
}
|
||||
if (lineCheck == newLines.Count)
|
||||
continue;
|
||||
lineCheck++;
|
||||
}
|
||||
if (lineCheck == newLines.Length)
|
||||
if (string.IsNullOrEmpty(checkDirectory))
|
||||
continue;
|
||||
checkDirectory = Path.Combine(checkDirectory, DateTime.Now.Ticks.ToString());
|
||||
_ = Directory.CreateDirectory(checkDirectory);
|
||||
Thread.Sleep(500);
|
||||
Directory.Delete(checkDirectory);
|
||||
reload = FileWrite(record, newLines, percent);
|
||||
if (!reloadAny && reload)
|
||||
reloadAny = true;
|
||||
}
|
||||
checkDirectory = Path.Combine(checkDirectory, DateTime.Now.Ticks.ToString());
|
||||
_ = Directory.CreateDirectory(checkDirectory);
|
||||
Thread.Sleep(500);
|
||||
Directory.Delete(checkDirectory);
|
||||
lines = record.Lines.ToList();
|
||||
for (int i = record.StopLine.Value - 1; i > record.SubTasksLine.Value + 1; i--)
|
||||
lines.RemoveAt(i);
|
||||
if (record.StopLine.Value == record.Lines.Length && lines[^1].Length == 0)
|
||||
lines.RemoveAt(lines.Count - 1);
|
||||
for (int i = 0; i < newLines.Length; i++)
|
||||
lines.Insert(record.SubTasksLine.Value + 1 + i, newLines[i]);
|
||||
lines.Insert(record.SubTasksLine.Value + 1, string.Empty);
|
||||
File.WriteAllLines(record.File, lines);
|
||||
if (!reloadAny)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,206 +0,0 @@
|
||||
using File_Folder_Helper.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace File_Folder_Helper.ADO2024.PI2;
|
||||
|
||||
internal static partial class Helper20240724
|
||||
{
|
||||
|
||||
private record FileConnectorConfigurationSystem(string AlternateTargetFolder,
|
||||
string FileAgeThreshold,
|
||||
string[] SourceFileFilters,
|
||||
string TargetFileLocation);
|
||||
|
||||
#pragma warning disable IDE0028, IDE0056, IDE0300, IDE0240, IDE0241
|
||||
|
||||
private static readonly HttpClient _HttpClient = new();
|
||||
private static readonly string _StaticFileServer = "localhost:5054";
|
||||
private static readonly FileConnectorConfigurationSystem _FileConnectorConfiguration = new(
|
||||
"D:/Tmp/Phares/AlternateTargetFolder",
|
||||
"000:20:00:01",
|
||||
[".txt"],
|
||||
"D:/Tmp/Phares/TargetFileLocation");
|
||||
|
||||
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 string[] GetValidWeeks(DateTime fileAgeThresholdDateTime)
|
||||
{
|
||||
DateTime dateTime = DateTime.Now;
|
||||
Calendar calendar = new CultureInfo("en-US").Calendar;
|
||||
string weekOfYear = $"{dateTime:yyyy}_Week_{calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}";
|
||||
string lastWeekOfYear = $"{fileAgeThresholdDateTime:yyyy}_Week_{calendar.GetWeekOfYear(fileAgeThresholdDateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday):00}";
|
||||
return new string[] { weekOfYear, lastWeekOfYear }.Distinct().ToArray();
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<NginxFileSystem> GetDayNginxFileSystemCollection(DateTime fileAgeThresholdDateTime, string week, string day, string dayUrl, NginxFileSystem[] dayNginxFileSystemCollection)
|
||||
{
|
||||
List<NginxFileSystem> results = new();
|
||||
DateTime dateTime;
|
||||
string nginxFormat = "ddd, dd MMM yyyy HH:mm:ss zzz";
|
||||
foreach (NginxFileSystem dayNginxFileSystem in dayNginxFileSystemCollection)
|
||||
{
|
||||
if (!DateTime.TryParseExact(dayNginxFileSystem.MTime.Replace("GMT", "+00:00"), nginxFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
|
||||
continue;
|
||||
if (dateTime < fileAgeThresholdDateTime)
|
||||
continue;
|
||||
results.Add(new(
|
||||
Path.GetFullPath(Path.Combine(_FileConnectorConfiguration.TargetFileLocation, week, day, dayNginxFileSystem.Name)),
|
||||
string.Concat(dayUrl, '/', dayNginxFileSystem.Name),
|
||||
dateTime.ToString(),
|
||||
dayNginxFileSystem.Size));
|
||||
}
|
||||
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
|
||||
List<NginxFileSystem> results = new();
|
||||
string dayUrl;
|
||||
string dayJson;
|
||||
string weekJson;
|
||||
string checkWeek;
|
||||
Task<HttpResponseMessage> task;
|
||||
NginxFileSystem[]? dayNginxFileSystemCollection;
|
||||
NginxFileSystem[]? weekNginxFileSystemCollection;
|
||||
string[] days = GetValidDays(fileAgeThresholdDateTime);
|
||||
string[] weeks = GetValidWeeks(fileAgeThresholdDateTime);
|
||||
foreach (string week in weeks)
|
||||
{
|
||||
checkWeek = string.Concat("http://", _StaticFileServer, '/', week);
|
||||
task = _HttpClient.GetAsync(checkWeek);
|
||||
task.Wait();
|
||||
if (!task.Result.IsSuccessStatusCode)
|
||||
continue;
|
||||
weekJson = _HttpClient.GetStringAsync(checkWeek).Result;
|
||||
weekNginxFileSystemCollection = JsonSerializer.Deserialize(weekJson, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
|
||||
if (weekNginxFileSystemCollection is null)
|
||||
continue;
|
||||
foreach (NginxFileSystem weekNginxFileSystem in weekNginxFileSystemCollection)
|
||||
{
|
||||
if (!(from l in days where weekNginxFileSystem.Name == l select false).Any())
|
||||
continue;
|
||||
dayUrl = string.Concat(checkWeek, '/', weekNginxFileSystem.Name);
|
||||
dayJson = _HttpClient.GetStringAsync(dayUrl).Result;
|
||||
dayNginxFileSystemCollection = JsonSerializer.Deserialize(dayJson, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
|
||||
if (dayNginxFileSystemCollection is null)
|
||||
continue;
|
||||
results.AddRange(GetDayNginxFileSystemCollection(fileAgeThresholdDateTime, week, weekNginxFileSystem.Name, dayUrl, dayNginxFileSystemCollection));
|
||||
}
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
#nullable disable
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<Tuple<DateTime, FileInfo, FileInfo, string>> GetPossible()
|
||||
{
|
||||
List<Tuple<DateTime, FileInfo, FileInfo, string>> results = new();
|
||||
DateTime dateTime;
|
||||
FileInfo targetFileInfo;
|
||||
FileInfo alternateFileInfo;
|
||||
DateTime fileAgeThresholdDateTime = GetFileAgeThresholdDateTime(_FileConnectorConfiguration.FileAgeThreshold);
|
||||
ReadOnlyCollection<NginxFileSystem> dayNginxFileSystemCollection = GetDayNginxFileSystemCollection(fileAgeThresholdDateTime);
|
||||
foreach (NginxFileSystem nginxFileSystem in dayNginxFileSystemCollection)
|
||||
{
|
||||
targetFileInfo = new FileInfo(nginxFileSystem.Name);
|
||||
if (targetFileInfo.Directory is null)
|
||||
continue;
|
||||
if (!Directory.Exists(targetFileInfo.Directory.FullName))
|
||||
_ = Directory.CreateDirectory(targetFileInfo.Directory.FullName);
|
||||
if (!DateTime.TryParse(nginxFileSystem.MTime, out dateTime))
|
||||
continue;
|
||||
if (targetFileInfo.Exists && targetFileInfo.LastWriteTime == dateTime)
|
||||
continue;
|
||||
alternateFileInfo = new(Path.Combine(_FileConnectorConfiguration.AlternateTargetFolder, nginxFileSystem.Name));
|
||||
results.Add(new(dateTime, targetFileInfo, alternateFileInfo, nginxFileSystem.Type));
|
||||
}
|
||||
return (from l in results orderby l.Item1 select l).ToList().AsReadOnly();
|
||||
}
|
||||
|
||||
private static void Test()
|
||||
{
|
||||
#nullable enable
|
||||
if (_HttpClient is null)
|
||||
throw new Exception();
|
||||
if (string.IsNullOrEmpty(_StaticFileServer))
|
||||
throw new Exception();
|
||||
if (string.IsNullOrEmpty(_StaticFileServer))
|
||||
{
|
||||
ReadOnlyCollection<Tuple<DateTime, FileInfo, FileInfo, string>> possibleDownload = GetPossible();
|
||||
if (possibleDownload.Count > 0)
|
||||
{
|
||||
string targetFileName = possibleDownload[0].Item4;
|
||||
FileInfo targetFileInfo = possibleDownload[0].Item2;
|
||||
FileInfo alternateFileInfo = possibleDownload[0].Item3;
|
||||
DateTime matchNginxFileSystemDateTime = possibleDownload[0].Item1;
|
||||
// if (alternateFileInfo.Exists)
|
||||
// File.Delete(alternateFileInfo.FullName);
|
||||
if (targetFileInfo.Exists)
|
||||
File.Delete(targetFileInfo.FullName);
|
||||
string targetJson = _HttpClient.GetStringAsync(targetFileName).Result;
|
||||
File.WriteAllText(targetFileInfo.FullName, targetJson);
|
||||
targetFileInfo.LastWriteTime = matchNginxFileSystemDateTime;
|
||||
// File.Copy(targetFileInfo.FullName, alternateFileInfo.FullName);
|
||||
File.AppendAllText(alternateFileInfo.FullName, targetJson);
|
||||
}
|
||||
}
|
||||
#nullable disable
|
||||
}
|
||||
|
||||
internal static void CopyDirectories(ILogger<Worker> logger, List<string> args)
|
||||
{
|
||||
Test();
|
||||
string[] files;
|
||||
Process process;
|
||||
string checkDirectory;
|
||||
string filter = args[3];
|
||||
string replaceWith = args[4];
|
||||
string searchPattern = args[2];
|
||||
string sourceDirectory = Path.GetFullPath(args[0]);
|
||||
string[] foundDirectories = Directory.GetDirectories(sourceDirectory, searchPattern, SearchOption.AllDirectories);
|
||||
logger.LogInformation($"Found {foundDirectories.Length} directories");
|
||||
foreach (string foundDirectory in foundDirectories)
|
||||
{
|
||||
if (!foundDirectory.Contains(filter))
|
||||
continue;
|
||||
logger.LogDebug(foundDirectory);
|
||||
checkDirectory = foundDirectory.Replace(filter, replaceWith);
|
||||
if (Directory.Exists(checkDirectory))
|
||||
{
|
||||
files = Directory.GetFiles(checkDirectory, "*", SearchOption.AllDirectories);
|
||||
if (files.Length > 0)
|
||||
continue;
|
||||
Directory.Delete(checkDirectory);
|
||||
}
|
||||
process = Process.Start("cmd.exe", $"/c xCopy \"{foundDirectory}\" \"{checkDirectory}\" /S /E /I /H /Y");
|
||||
process.WaitForExit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
383
ADO2024/PI4/Helper-2024-12-17.cs
Normal file
383
ADO2024/PI4/Helper-2024-12-17.cs
Normal file
@ -0,0 +1,383 @@
|
||||
using DiscUtils.Iso9660;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Renci.SshNet;
|
||||
using Renci.SshNet.Sftp;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO.Compression;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace File_Folder_Helper.ADO2024.PI4;
|
||||
|
||||
internal static partial class Helper20241217
|
||||
{
|
||||
|
||||
private record Record(string Directory, Job? Job, string Path);
|
||||
private record Job(string AlternatePath, string Directory, string Extension, File[] Files, int FilesCount, double FilesTotalLength, int Keep, Target[] Targets);
|
||||
private record SecureShell(string Host, string Key, string Path, bool Required, string User);
|
||||
private record ServerMessageBlock(string Path, bool Required);
|
||||
private record Target(SecureShell? SecureShell, ServerMessageBlock? ServerMessageBlock);
|
||||
private record File(long LastWriteTicks, long Length, string RelativePath);
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(Job))]
|
||||
private partial class JobSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(File[]))]
|
||||
private partial class FilesSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
||||
private static IEnumerable<Record> GetRecords(string directory, string searchPattern)
|
||||
{
|
||||
Job? job;
|
||||
string json;
|
||||
Record record;
|
||||
string fileName;
|
||||
string directoryName;
|
||||
IEnumerable<string> files = Directory.EnumerateFiles(directory, searchPattern, new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true });
|
||||
foreach (string file in files)
|
||||
{
|
||||
fileName = Path.GetFileName(file);
|
||||
directoryName = Path.GetDirectoryName(file) ?? throw new Exception();
|
||||
if (!fileName.StartsWith('.'))
|
||||
{
|
||||
System.IO.File.Move(file, Path.Combine(directoryName, $".{fileName}"));
|
||||
continue;
|
||||
}
|
||||
json = System.IO.File.ReadAllText(file);
|
||||
job = JsonSerializer.Deserialize(json, JobSourceGenerationContext.Default.Job);
|
||||
record = new(directoryName, job, file);
|
||||
yield return record;
|
||||
}
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<File> GetFiles(string directory, string searchPattern, string[] ignoreFileNames)
|
||||
{
|
||||
List<File> results = [];
|
||||
File file;
|
||||
string relativePath;
|
||||
string[] files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
|
||||
FileInfo[] fileInfoCollection = files.Select(l => new FileInfo(l)).ToArray();
|
||||
foreach (FileInfo fileInfo in fileInfoCollection)
|
||||
{
|
||||
if (fileInfo.Name == searchPattern)
|
||||
continue;
|
||||
if (ignoreFileNames.Any(l => l == fileInfo.Name))
|
||||
continue;
|
||||
if (!string.IsNullOrEmpty(fileInfo.LinkTarget))
|
||||
continue;
|
||||
relativePath = Path.GetRelativePath(directory, fileInfo.FullName).Replace(';', '_');
|
||||
if (relativePath.StartsWith(".."))
|
||||
relativePath = relativePath[3..];
|
||||
file = new(fileInfo.LastWriteTime.Ticks, fileInfo.Length, relativePath);
|
||||
results.Add(file);
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<File> GetFiles(string searchPattern, string[] ignoreFileNames, Record record) =>
|
||||
GetFiles(record.Directory, searchPattern, ignoreFileNames);
|
||||
|
||||
private static string? GetJsonIfNotEqual(string searchPattern, string[] ignoreFileNames, Record record, Job job, ReadOnlyCollection<File> files)
|
||||
{
|
||||
string? result;
|
||||
string? jsonNew;
|
||||
string? jsonOld;
|
||||
string fileName;
|
||||
int ignoreCount = 0;
|
||||
double filesTotalLengthNew = 0;
|
||||
File[] filesArray = files.ToArray();
|
||||
double filesTotalLengthOld = job.FilesTotalLength;
|
||||
foreach (File file in files)
|
||||
filesTotalLengthNew += file.Length;
|
||||
Job jobNew = new(job.AlternatePath,
|
||||
record.Directory,
|
||||
job.Extension,
|
||||
filesArray,
|
||||
files.Count,
|
||||
filesTotalLengthNew,
|
||||
job.Keep,
|
||||
job.Targets);
|
||||
result = JsonSerializer.Serialize(jobNew, JobSourceGenerationContext.Default.Job);
|
||||
if (filesTotalLengthNew != filesTotalLengthOld)
|
||||
{
|
||||
filesTotalLengthOld = 0;
|
||||
foreach (File file in job.Files)
|
||||
{
|
||||
fileName = Path.GetFileName(file.RelativePath);
|
||||
if (fileName == searchPattern || ignoreFileNames.Any(l => l == fileName))
|
||||
{
|
||||
ignoreCount += 1;
|
||||
continue;
|
||||
}
|
||||
if (file.Length == 0)
|
||||
{
|
||||
ignoreCount += 1;
|
||||
continue;
|
||||
}
|
||||
filesTotalLengthOld += file.Length;
|
||||
}
|
||||
}
|
||||
if (filesTotalLengthNew != filesTotalLengthOld || files.Count != (job.Files.Length - ignoreCount))
|
||||
{
|
||||
jsonNew = null;
|
||||
jsonOld = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonNew = JsonSerializer.Serialize((from l in filesArray orderby l.RelativePath.Length, l.RelativePath select l).ToArray(), FilesSourceGenerationContext.Default.FileArray);
|
||||
jsonOld = JsonSerializer.Serialize((from l in job.Files orderby l.RelativePath.Length, l.RelativePath where l.RelativePath != searchPattern select l).ToArray(), FilesSourceGenerationContext.Default.FileArray);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(jsonNew) && !string.IsNullOrEmpty(jsonOld) && jsonNew == jsonOld)
|
||||
result = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void WriteISO(Record record, ReadOnlyCollection<File> files, string path, string directoryName)
|
||||
{
|
||||
CDBuilder builder = new() { UseJoliet = true, VolumeIdentifier = directoryName.Length < 25 ? directoryName : directoryName[..25] };
|
||||
foreach (File file in files)
|
||||
_ = builder.AddFile(file.RelativePath, Path.Combine(record.Directory, file.RelativePath));
|
||||
builder.Build(path);
|
||||
}
|
||||
|
||||
private static void WriteZIP(Record record, ReadOnlyCollection<File> files, string path)
|
||||
{
|
||||
using ZipArchive zip = ZipFile.Open(path, ZipArchiveMode.Create);
|
||||
string directoryEntry;
|
||||
List<string> directoryEntries = [];
|
||||
foreach (File file in files)
|
||||
{
|
||||
directoryEntry = Path.GetDirectoryName(file.RelativePath) ?? throw new Exception();
|
||||
if (!directoryEntries.Contains(directoryEntry))
|
||||
continue;
|
||||
directoryEntries.Add(directoryEntry);
|
||||
_ = zip.CreateEntry(file.RelativePath);
|
||||
}
|
||||
foreach (File file in files)
|
||||
_ = zip.CreateEntryFromFile(Path.Combine(record.Directory, file.RelativePath), file.RelativePath);
|
||||
}
|
||||
|
||||
private static void WriteExtension(Record record, Job job, ReadOnlyCollection<File> files, string path)
|
||||
{
|
||||
string directoryName = Path.GetFileName(record.Directory);
|
||||
if (job.Extension.Equals(".iso", StringComparison.OrdinalIgnoreCase))
|
||||
WriteISO(record, files, path, directoryName);
|
||||
else if (job.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase))
|
||||
WriteZIP(record, files, path);
|
||||
else
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static void PushTo(Job job, SecureShell secureShell, string path)
|
||||
{
|
||||
string remotePath = string.Concat(secureShell.Path, '/', Path.GetFileName(path));
|
||||
using SftpClient client = new(secureShell.Host, secureShell.User, new PrivateKeyFile(secureShell.Key));
|
||||
client.Connect();
|
||||
if (job.Files.Length == 0)
|
||||
{
|
||||
string directoryName = Path.GetDirectoryName(secureShell.Path) ?? throw new Exception();
|
||||
try
|
||||
{ client.CreateDirectory(Path.GetDirectoryName(directoryName) ?? throw new Exception()); }
|
||||
catch (Exception) { }
|
||||
try
|
||||
{ client.CreateDirectory(directoryName); }
|
||||
catch (Exception) { }
|
||||
try
|
||||
{ client.CreateDirectory(secureShell.Path); }
|
||||
catch (Exception) { }
|
||||
}
|
||||
using FileStream fileStream = System.IO.File.OpenRead(path);
|
||||
client.UploadFile(fileStream, remotePath);
|
||||
}
|
||||
|
||||
private static void PushTo(ServerMessageBlock serverMessageBlock, string path)
|
||||
{
|
||||
string remotePath = Path.Combine(serverMessageBlock.Path, Path.GetFileName(path));
|
||||
System.IO.File.Copy(path, remotePath);
|
||||
}
|
||||
|
||||
private static void PushTo(string directory, string path)
|
||||
{
|
||||
string remotePath = Path.Combine(directory, Path.GetFileName(path));
|
||||
System.IO.File.Copy(path, remotePath);
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<Exception> PushTo(Job job, string path)
|
||||
{
|
||||
List<Exception> results = [];
|
||||
foreach (Target target in job.Targets)
|
||||
{
|
||||
if (target.SecureShell is not null)
|
||||
{
|
||||
try
|
||||
{ PushTo(job, target.SecureShell, path); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (target.SecureShell.Required)
|
||||
results.Add(ex);
|
||||
}
|
||||
}
|
||||
else if (target.ServerMessageBlock is not null)
|
||||
{
|
||||
try
|
||||
{ PushTo(target.ServerMessageBlock, path); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (target.ServerMessageBlock.Required)
|
||||
results.Add(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
private static void DeleteOld(Job job, SecureShell secureShell, string path)
|
||||
{
|
||||
List<string> results = [];
|
||||
using SftpClient client = new(secureShell.Host, secureShell.User, new PrivateKeyFile(secureShell.Key));
|
||||
client.Connect();
|
||||
foreach (ISftpFile file in client.ListDirectory(secureShell.Path))
|
||||
{
|
||||
if (file.Name == path)
|
||||
continue;
|
||||
if (!file.Name.EndsWith(job.Extension, StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
results.Add(file.FullName);
|
||||
}
|
||||
for (int i = job.Keep - 1; i < results.Count; i++)
|
||||
client.DeleteFile(results[i]);
|
||||
}
|
||||
|
||||
private static void DeleteOld(Job job, ServerMessageBlock serverMessageBlock, string path)
|
||||
{
|
||||
List<string> results = [];
|
||||
string[] files = Directory.GetFiles(serverMessageBlock.Path, $"*{job.Extension}", SearchOption.TopDirectoryOnly);
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (file == path)
|
||||
continue;
|
||||
results.Add(file);
|
||||
}
|
||||
for (int i = job.Keep - 1; i < results.Count; i++)
|
||||
System.IO.File.Delete(results[i]);
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<Exception> DeleteOld(Job job, string path)
|
||||
{
|
||||
List<Exception> results = [];
|
||||
foreach (Target target in job.Targets)
|
||||
{
|
||||
if (target.SecureShell is not null)
|
||||
{
|
||||
try
|
||||
{ DeleteOld(job, target.SecureShell, path); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (target.SecureShell.Required)
|
||||
results.Add(ex);
|
||||
}
|
||||
}
|
||||
else if (target.ServerMessageBlock is not null)
|
||||
{
|
||||
try
|
||||
{ DeleteOld(job, target.ServerMessageBlock, path); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (target.ServerMessageBlock.Required)
|
||||
results.Add(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
private static void Verify(string searchPattern, string[] ignoreFileNames)
|
||||
{
|
||||
List<Target> targets = [
|
||||
new(new SecureShell("free.file.sync.root", "C:/Users/phares/.ssh/id_ed25519", "\\home", true, "root"), null),
|
||||
new(null, new ServerMessageBlock("\\\\mesfs.infineon.com\\EC_APC\\DEV", true))
|
||||
];
|
||||
string directory = Path.Combine(Environment.CurrentDirectory, ".vscode", "helper");
|
||||
if (!Directory.Exists(directory))
|
||||
_ = Directory.CreateDirectory(directory);
|
||||
ReadOnlyCollection<File> files = GetFiles(directory, searchPattern, ignoreFileNames);
|
||||
double filesTotalLength = 0;
|
||||
foreach (File file in files)
|
||||
filesTotalLength += file.Length;
|
||||
Job job = new(
|
||||
"C:/Users/phares",
|
||||
directory,
|
||||
"*.iso",
|
||||
files.ToArray(),
|
||||
files.Count,
|
||||
filesTotalLength,
|
||||
3,
|
||||
targets.ToArray());
|
||||
string json = JsonSerializer.Serialize(job, JobSourceGenerationContext.Default.Job);
|
||||
System.IO.File.WriteAllText(Path.Combine(directory, "verify.json"), json);
|
||||
}
|
||||
|
||||
internal static void Backup(ILogger<Worker> logger, List<string> args)
|
||||
{
|
||||
string path;
|
||||
string? json;
|
||||
string directoryName;
|
||||
ReadOnlyCollection<File> files;
|
||||
string searchPattern = args[2];
|
||||
ReadOnlyCollection<Exception> exceptions;
|
||||
string[] ignoreFileNames = args[3].Split('|');
|
||||
string sourceDirectory = Path.GetFullPath(args[0]);
|
||||
logger.LogInformation("Searching <{sourceDirectory}> with search pattern {searchPattern}", args[0], searchPattern);
|
||||
if (Debugger.IsAttached)
|
||||
Verify(searchPattern, ignoreFileNames);
|
||||
IEnumerable<Record> records = GetRecords(sourceDirectory, searchPattern);
|
||||
foreach (Record record in records)
|
||||
{
|
||||
if (record.Job is null || record.Job.Targets.Length == 0 || string.IsNullOrEmpty(record.Job.Extension))
|
||||
continue;
|
||||
logger.LogInformation("Searching <{directory}>", record.Directory);
|
||||
files = GetFiles(searchPattern, ignoreFileNames, record);
|
||||
json = GetJsonIfNotEqual(searchPattern, ignoreFileNames, record, record.Job, files);
|
||||
if (string.IsNullOrEmpty(json))
|
||||
continue;
|
||||
directoryName = Path.GetFileName(record.Directory);
|
||||
path = Path.Combine(record.Directory, $"{directoryName}-{DateTime.Now:yyyy-MM-dd-HH-mm-ss-fff}{record.Job.Extension}");
|
||||
logger.LogInformation("Writing <{directory}> extension", record.Directory);
|
||||
WriteExtension(record, record.Job, files, path);
|
||||
logger.LogInformation("Pushing <{directory}> extension", record.Directory);
|
||||
exceptions = PushTo(record.Job, path);
|
||||
if (exceptions.Count != 0)
|
||||
{
|
||||
foreach (Exception exception in exceptions)
|
||||
logger.LogError(exception, exception.Message);
|
||||
PushTo(record.Job.AlternatePath, path);
|
||||
}
|
||||
System.IO.File.WriteAllText(record.Path, json);
|
||||
System.IO.File.Delete(path);
|
||||
logger.LogInformation("Deleting old <{directory}> extension", record.Directory);
|
||||
exceptions = DeleteOld(record.Job, path);
|
||||
if (exceptions.Count != 0)
|
||||
{
|
||||
foreach (Exception exception in exceptions)
|
||||
logger.LogError(exception, exception.Message);
|
||||
}
|
||||
}
|
||||
if (Debugger.IsAttached && records.Count() == 0)
|
||||
{
|
||||
files = GetFiles(sourceDirectory, searchPattern, ignoreFileNames);
|
||||
json = JsonSerializer.Serialize(files.ToArray(), FilesSourceGenerationContext.Default.FileArray);
|
||||
System.IO.File.WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", ".json"), json);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
195
ADO2024/PI4/Helper-2024-12-24.cs
Normal file
195
ADO2024/PI4/Helper-2024-12-24.cs
Normal file
@ -0,0 +1,195 @@
|
||||
using File_Folder_Helper.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
#if ShellProgressBar
|
||||
using ShellProgressBar;
|
||||
#endif
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace File_Folder_Helper.ADO2024.PI4;
|
||||
|
||||
internal static partial class Helper20241224
|
||||
{
|
||||
|
||||
private static readonly HttpClient _HttpClient = new();
|
||||
|
||||
private record Record(Uri URI, string Path, DateTime LastModified);
|
||||
|
||||
private static ReadOnlyCollection<NginxFileSystem>? GetRecursiveCollection(string host, string page)
|
||||
{
|
||||
List<NginxFileSystem>? results;
|
||||
Uri uri = new($"https://{host}/{page}");
|
||||
string format = NginxFileSystem.GetFormat();
|
||||
TimeZoneInfo timeZoneInfo = TimeZoneInfo.Local;
|
||||
Task<HttpResponseMessage> taskHttpResponseMessage = _HttpClient.GetAsync(uri);
|
||||
taskHttpResponseMessage.Wait();
|
||||
if (!taskHttpResponseMessage.Result.IsSuccessStatusCode)
|
||||
results = null;
|
||||
else
|
||||
{
|
||||
Task<string> taskString = taskHttpResponseMessage.Result.Content.ReadAsStringAsync();
|
||||
taskString.Wait();
|
||||
NginxFileSystem[]? nginxFileSystems = JsonSerializer.Deserialize(taskString.Result, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
|
||||
if (nginxFileSystems is null)
|
||||
results = null;
|
||||
else
|
||||
{
|
||||
results = [];
|
||||
NginxFileSystem nginxFileSystem;
|
||||
ReadOnlyCollection<NginxFileSystem>? directory;
|
||||
for (int i = 0; i < nginxFileSystems.Length; i++)
|
||||
{
|
||||
nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
|
||||
if (nginxFileSystem.Type == "file")
|
||||
results.Add(nginxFileSystem);
|
||||
else
|
||||
{
|
||||
directory = GetRecursiveCollection(host, $"{page}/{nginxFileSystem.Name}");
|
||||
if (directory is null)
|
||||
continue;
|
||||
results.AddRange(directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results?.AsReadOnly();
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<NginxFileSystem>? GetCollection(string format, TimeZoneInfo timeZoneInfo, Uri uri)
|
||||
{
|
||||
List<NginxFileSystem>? results;
|
||||
Task<HttpResponseMessage> taskHttpResponseMessage = _HttpClient.GetAsync(uri);
|
||||
taskHttpResponseMessage.Wait();
|
||||
if (!taskHttpResponseMessage.Result.IsSuccessStatusCode)
|
||||
results = null;
|
||||
else
|
||||
{
|
||||
Task<string> taskString = taskHttpResponseMessage.Result.Content.ReadAsStringAsync();
|
||||
taskString.Wait();
|
||||
NginxFileSystem[]? nginxFileSystems = JsonSerializer.Deserialize(taskString.Result, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
|
||||
if (nginxFileSystems is null)
|
||||
results = null;
|
||||
else
|
||||
{
|
||||
results = [];
|
||||
NginxFileSystem nginxFileSystem;
|
||||
for (int i = 0; i < nginxFileSystems.Length; i++)
|
||||
{
|
||||
nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
|
||||
results.Add(nginxFileSystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results?.AsReadOnly();
|
||||
}
|
||||
|
||||
private static Record? CompareFile(string host, ReadOnlyCollection<string> directoryNames, string compareDirectory, NginxFileSystem nginxFileSystem)
|
||||
{
|
||||
Record? result;
|
||||
if (nginxFileSystem.LastModified is null || nginxFileSystem.Length is null)
|
||||
result = null;
|
||||
else
|
||||
{
|
||||
Uri uri = new($"https://{host}/{string.Join('/', directoryNames)}/{nginxFileSystem.Name}");
|
||||
FileInfo fileInfo = new($"{compareDirectory}\\{string.Join('\\', directoryNames)}\\{nginxFileSystem.Name}");
|
||||
if (!fileInfo.Exists || fileInfo.Length != nginxFileSystem.Length.Value)
|
||||
result = new(uri, fileInfo.FullName, nginxFileSystem.LastModified.Value);
|
||||
else
|
||||
{
|
||||
double totalSeconds = new TimeSpan(fileInfo.LastWriteTime.Ticks - nginxFileSystem.LastModified.Value.Ticks).TotalSeconds;
|
||||
if (totalSeconds is < 2 and > -2)
|
||||
result = null;
|
||||
else
|
||||
result = new(uri, fileInfo.FullName, nginxFileSystem.LastModified.Value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<Record> CompareDirectory(string format, TimeZoneInfo timeZoneInfo, string host, ReadOnlyCollection<string> directoryNames, string compareDirectory, NginxFileSystem nginxFileSystem)
|
||||
{
|
||||
ReadOnlyCollection<Record> results;
|
||||
List<string> collection = directoryNames.ToList();
|
||||
collection.Add(nginxFileSystem.Name);
|
||||
results = GetRecord(format, timeZoneInfo, host, collection.AsReadOnly(), compareDirectory);
|
||||
return results;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<Record> GetRecord(string format, TimeZoneInfo timeZoneInfo, string host, ReadOnlyCollection<string> directoryNames, string compareDirectory)
|
||||
{
|
||||
List<Record> results = [];
|
||||
Uri uri = new($"https://{host}/{string.Join('/', directoryNames)}");
|
||||
ReadOnlyCollection<NginxFileSystem>? nginxFileSystems = GetCollection(format, timeZoneInfo, uri);
|
||||
if (nginxFileSystems is not null)
|
||||
{
|
||||
NginxFileSystem nginxFileSystem;
|
||||
ReadOnlyCollection<Record> records;
|
||||
string checkDirectory = $"{compareDirectory}\\{string.Join('\\', directoryNames)}";
|
||||
if (!Directory.Exists(checkDirectory))
|
||||
_ = Directory.CreateDirectory(checkDirectory);
|
||||
for (int i = 0; i < nginxFileSystems.Count; i++)
|
||||
{
|
||||
nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
|
||||
if (nginxFileSystem.Type == "file")
|
||||
{
|
||||
Record? record = CompareFile(host, directoryNames, compareDirectory, nginxFileSystem);
|
||||
if (record is not null)
|
||||
results.Add(record);
|
||||
}
|
||||
else
|
||||
{
|
||||
records = CompareDirectory(format, timeZoneInfo, host, directoryNames, compareDirectory, nginxFileSystem);
|
||||
foreach (Record record in records)
|
||||
results.Add(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results.AsReadOnly();
|
||||
}
|
||||
|
||||
private static void Download(Record record)
|
||||
{
|
||||
Task<HttpResponseMessage> taskHttpResponseMessage = _HttpClient.GetAsync(record.URI);
|
||||
taskHttpResponseMessage.Wait();
|
||||
if (taskHttpResponseMessage.Result.IsSuccessStatusCode)
|
||||
{
|
||||
Task<string> taskString = taskHttpResponseMessage.Result.Content.ReadAsStringAsync();
|
||||
taskString.Wait();
|
||||
File.WriteAllText(record.Path, taskString.Result);
|
||||
File.SetLastWriteTime(record.Path, record.LastModified);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Compare(ILogger<Worker> logger, List<string> args)
|
||||
{
|
||||
string host = args[2];
|
||||
string rootDirectoryName = args[3];
|
||||
string format = NginxFileSystem.GetFormat();
|
||||
TimeZoneInfo timeZoneInfo = TimeZoneInfo.Local;
|
||||
string compareDirectory = Path.GetFullPath(args[0]);
|
||||
logger.LogInformation("Comparing files on {host}", host);
|
||||
ReadOnlyCollection<Record> records = GetRecord(format, timeZoneInfo, host, new([rootDirectoryName]), compareDirectory);
|
||||
#if ShellProgressBar
|
||||
ProgressBar progressBar = new(records.Count, "Downloading", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
|
||||
#endif
|
||||
foreach (Record record in records)
|
||||
{
|
||||
#if ShellProgressBar
|
||||
progressBar.Tick();
|
||||
#endif
|
||||
Download(record);
|
||||
}
|
||||
#if ShellProgressBar
|
||||
progressBar.Dispose();
|
||||
#endif
|
||||
if (Debugger.IsAttached)
|
||||
{
|
||||
ReadOnlyCollection<NginxFileSystem>? recursiveCollection = GetRecursiveCollection(host, rootDirectoryName);
|
||||
string? json = recursiveCollection is null ? null : JsonSerializer.Serialize(recursiveCollection.ToArray(), NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
|
||||
if (!string.IsNullOrEmpty(json))
|
||||
File.WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", ".json"), json);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -81,8 +81,6 @@ internal static class HelperDay
|
||||
ADO2024.PI2.Helper20240711.GitRemoteRemove(logger, args);
|
||||
else if (args[1] == "Day-Helper-2024-07-18")
|
||||
ADO2024.PI2.Helper20240718.JsonToMarkdown(logger, args);
|
||||
else if (args[1] == "Day-Helper-2024-07-24")
|
||||
ADO2024.PI2.Helper20240724.CopyDirectories(logger, args);
|
||||
else if (args[1] == "Day-Helper-2024-07-28")
|
||||
ADO2024.PI2.Helper20240728.DownloadSslCertificates(logger, args);
|
||||
else if (args[1] == "Day-Helper-2024-08-05")
|
||||
@ -123,6 +121,10 @@ internal static class HelperDay
|
||||
ADO2024.PI4.Helper20241204.ConvertToUTF8(logger, args);
|
||||
else if (args[1] == "Day-Helper-2024-12-12")
|
||||
ADO2024.PI4.Helper20241212.Rename(logger, args);
|
||||
else if (args[1] == "Day-Helper-2024-12-17")
|
||||
ADO2024.PI4.Helper20241217.Backup(logger, args);
|
||||
else if (args[1] == "Day-Helper-2024-12-24")
|
||||
ADO2024.PI4.Helper20241224.Compare(logger, args);
|
||||
else
|
||||
throw new Exception(appSettings.Company);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Exe</OutputType>
|
||||
@ -18,9 +18,10 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
|
||||
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.11" />
|
||||
<PackageReference Include="SSH.NET" Version="2024.2.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||
<PackageReference Include="TextCopy" Version="6.2.1" />
|
||||
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
|
||||
<PackageReference Include="YamlDotNet" Version="16.2.0" />
|
||||
<PackageReference Include="YamlDotNet" Version="16.2.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -133,31 +133,30 @@ internal static partial class HelperKanbanMetadata
|
||||
List<string> results = [kanbanIndexH1, string.Empty];
|
||||
string h1;
|
||||
TimeSpan timeSpan;
|
||||
List<string> lines;
|
||||
LineNumber lineNumber;
|
||||
Record[] sorted = (from l in records orderby l.GroupCount, l.FileInfo.LastWriteTime descending select l).ToArray();
|
||||
foreach (Record record in sorted)
|
||||
{
|
||||
if (record.ItemLineNumber == 0)
|
||||
throw new NotSupportedException();
|
||||
(lines, lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(record.FileInfo);
|
||||
if (lines.Count == 0)
|
||||
lineNumber = HelperMarkdown.GetLineNumbers(record.FileInfo);
|
||||
if (lineNumber.Lines.Count == 0)
|
||||
continue;
|
||||
timeSpan = new(record.FileInfo.LastWriteTime.Ticks - record.FileInfo.CreationTime.Ticks);
|
||||
h1 = lineNumber.H1 is null ? Path.GetFileNameWithoutExtension(record.FileInfo.Name) : lines[lineNumber.H1.Value];
|
||||
h1 = lineNumber.H1 is null ? Path.GetFileNameWithoutExtension(record.FileInfo.Name) : lineNumber.Lines[lineNumber.H1.Value];
|
||||
results.Add($"#{h1}");
|
||||
results.Add(string.Empty);
|
||||
results.Add("```yaml");
|
||||
results.Add($"CreationTime: {record.FileInfo.CreationTime:yyyy-MM-dd}");
|
||||
results.Add($"LastWriteTime: {record.FileInfo.LastWriteTime:yyyy-MM-dd}");
|
||||
results.Add($"TotalDays: {Math.Round(timeSpan.TotalDays, 2)}");
|
||||
if (lineNumber.FrontMatterYamlEnd is not null && lines.Count >= lineNumber.FrontMatterYamlEnd.Value)
|
||||
if (lineNumber.FrontMatterYamlEnd is not null && lineNumber.Lines.Count >= lineNumber.FrontMatterYamlEnd.Value)
|
||||
{
|
||||
for (int i = 0; i < lineNumber.FrontMatterYamlEnd; i++)
|
||||
{
|
||||
if (lines[i] == "---")
|
||||
if (lineNumber.Lines[i] == "---")
|
||||
continue;
|
||||
results.Add(lines[i]);
|
||||
results.Add(lineNumber.Lines[i]);
|
||||
}
|
||||
}
|
||||
results.Add($"status: {record.GroupCount}-{record.Group}");
|
||||
@ -175,7 +174,7 @@ internal static partial class HelperKanbanMetadata
|
||||
File.WriteAllText(file, string.Join(Environment.NewLine, results));
|
||||
}
|
||||
|
||||
internal static void SetMetadata(string sourceDirectory, ReadOnlyCollection<string> kanbanIndexFileLines, LineNumber kanbanIndexFileLineNumber, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
|
||||
internal static void SetMetadata(string sourceDirectory, LineNumber kanbanIndexFileLineNumber, ReadOnlyCollection<string> gitOthersModifiedAndDeletedExcludingStandardFiles)
|
||||
{
|
||||
bool? match;
|
||||
bool gitCheck;
|
||||
@ -184,23 +183,24 @@ internal static partial class HelperKanbanMetadata
|
||||
List<string> lines;
|
||||
LineNumber lineNumber;
|
||||
string? directory = Path.GetDirectoryName(sourceDirectory);
|
||||
List<Record> records = GetCollectionFromIndex(sourceDirectory, kanbanIndexFileLines);
|
||||
List<Record> records = GetCollectionFromIndex(sourceDirectory, kanbanIndexFileLineNumber.Lines);
|
||||
if (directory is not null && kanbanIndexFileLineNumber.H1 is not null)
|
||||
{
|
||||
string checkDirectory = Path.Combine(directory, ".vscode", "helper");
|
||||
if (Directory.Exists(checkDirectory))
|
||||
{
|
||||
WriteKanbanBoardFile(checkDirectory, records, kanbanIndexFileLines[kanbanIndexFileLineNumber.H1.Value]);
|
||||
WriteKanbanBoardYmlView(checkDirectory, records, kanbanIndexFileLines[kanbanIndexFileLineNumber.H1.Value]);
|
||||
WriteKanbanBoardFile(checkDirectory, records, kanbanIndexFileLineNumber.Lines[kanbanIndexFileLineNumber.H1.Value]);
|
||||
WriteKanbanBoardYmlView(checkDirectory, records, kanbanIndexFileLineNumber.Lines[kanbanIndexFileLineNumber.H1.Value]);
|
||||
}
|
||||
}
|
||||
foreach (Record record in records)
|
||||
{
|
||||
if (record.ItemLineNumber == 0)
|
||||
throw new NotSupportedException();
|
||||
(lines, lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(record.FileInfo);
|
||||
if (lines.Count == 0)
|
||||
lineNumber = HelperMarkdown.GetLineNumbers(record.FileInfo);
|
||||
if (lineNumber.Lines.Count == 0)
|
||||
continue;
|
||||
lines = lineNumber.Lines.ToList();
|
||||
statusLine = $"status: {record.GroupCount}-{record.Group}";
|
||||
paramCase = lineNumber.H1 is null ? null : GetParamCase(lines[lineNumber.H1.Value]);
|
||||
match = lineNumber.H1 is null || paramCase is null ? null : Path.GetFileNameWithoutExtension(record.FileInfo.Name) == paramCase;
|
||||
@ -235,8 +235,8 @@ internal static partial class HelperKanbanMetadata
|
||||
else
|
||||
{
|
||||
FileInfo fileInfo = new(indexFile);
|
||||
(List<string> lines, LineNumber lineNumber) = HelperMarkdown.GetStatusAndFrontMatterYamlEndLineNumbers(fileInfo);
|
||||
SetMetadata(fullPath, new(lines), lineNumber, gitOthersModifiedAndDeletedExcludingStandardFiles: new([]));
|
||||
LineNumber lineNumber = HelperMarkdown.GetLineNumbers(fileInfo);
|
||||
SetMetadata(fullPath, lineNumber, gitOthersModifiedAndDeletedExcludingStandardFiles: new([]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,7 @@ internal static partial class HelperMarkdown
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<string> GetFromMatterYamlLines(List<string> lines, LineNumber lineNumber)
|
||||
private static ReadOnlyCollection<string> GetFromMatterYamlLines(ReadOnlyCollection<string> lines, LineNumber lineNumber)
|
||||
{
|
||||
List<string> results = [];
|
||||
if (lineNumber.FrontMatterYamlEnd is not null && lines.Count >= lineNumber.FrontMatterYamlEnd.Value)
|
||||
@ -307,7 +307,7 @@ internal static partial class HelperMarkdown
|
||||
return new(results);
|
||||
}
|
||||
|
||||
internal static (List<string>, LineNumber) GetStatusAndFrontMatterYamlEndLineNumbers(FileInfo fileInfo)
|
||||
internal static LineNumber GetLineNumbers(FileInfo fileInfo)
|
||||
{
|
||||
string line;
|
||||
int? h1LineNumber = null;
|
||||
@ -315,6 +315,8 @@ internal static partial class HelperMarkdown
|
||||
int? statusLineNumber = null;
|
||||
int? createdLineNumber = null;
|
||||
int? updatedLineNumber = null;
|
||||
int? progressLineNumber = null;
|
||||
int? completedLineNumber = null;
|
||||
int? frontMatterYamlEndLineNumber = null;
|
||||
Encoding? encoding = GetEncoding(fileInfo.FullName) ?? Encoding.Default;
|
||||
string[] lines = File.ReadAllLines(fileInfo.FullName, encoding);
|
||||
@ -350,6 +352,16 @@ internal static partial class HelperMarkdown
|
||||
updatedLineNumber = i;
|
||||
continue;
|
||||
}
|
||||
if (line.Length > 10 && line[..10] == "progress: ")
|
||||
{
|
||||
progressLineNumber = i;
|
||||
continue;
|
||||
}
|
||||
if (line.Length > 11 && line[..11] == "completed: ")
|
||||
{
|
||||
completedLineNumber = i;
|
||||
continue;
|
||||
}
|
||||
if (h1LineNumber is null && line.Length > 2 && line[0] == '#' && line[1] == ' ')
|
||||
{
|
||||
h1LineNumber = i;
|
||||
@ -357,12 +369,15 @@ internal static partial class HelperMarkdown
|
||||
}
|
||||
}
|
||||
LineNumber lineNumber = new(createdLineNumber,
|
||||
completedLineNumber,
|
||||
h1LineNumber,
|
||||
frontMatterYamlEndLineNumber,
|
||||
lines.AsReadOnly(),
|
||||
progressLineNumber,
|
||||
statusLineNumber,
|
||||
typeLineNumber,
|
||||
updatedLineNumber);
|
||||
return (lines.ToList(), lineNumber);
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
private static Dictionary<string, object> GetFromMatterYaml(ReadOnlyCollection<string> frontMatterYamlLines)
|
||||
@ -387,7 +402,7 @@ internal static partial class HelperMarkdown
|
||||
return results;
|
||||
}
|
||||
|
||||
private static ReadOnlyDictionary<string, object> GetFromMatterYaml(List<string> lines, LineNumber lineNumber)
|
||||
private static ReadOnlyDictionary<string, object> GetFromMatterYaml(ReadOnlyCollection<string> lines, LineNumber lineNumber)
|
||||
{
|
||||
Dictionary<string, object> results = [];
|
||||
#pragma warning disable IL3050
|
||||
@ -660,12 +675,12 @@ internal static partial class HelperMarkdown
|
||||
string key;
|
||||
string type;
|
||||
bool isKanbanIndex;
|
||||
List<string> lines;
|
||||
bool isWithinSource;
|
||||
bool isKanbanMarkdown;
|
||||
LineNumber lineNumber;
|
||||
MarkdownFile markdownFile;
|
||||
string fileNameWithoutExtension;
|
||||
ReadOnlyCollection<string> lines;
|
||||
ReadOnlyDictionary<string, object> frontMatterYaml;
|
||||
bool isGitOthersModifiedAndDeletedExcludingStandard;
|
||||
ReadOnlyCollection<FileInfo> files = GetFiles(appSettings, input);
|
||||
@ -678,7 +693,8 @@ internal static partial class HelperMarkdown
|
||||
isGitOthersModifiedAndDeletedExcludingStandard = gitOthersModifiedAndDeletedExcludingStandardFiles.Contains(fileInfo.FullName);
|
||||
if (!isWithinSource && results.ContainsKey(key))
|
||||
continue;
|
||||
(lines, lineNumber) = GetStatusAndFrontMatterYamlEndLineNumbers(fileInfo);
|
||||
lineNumber = GetLineNumbers(fileInfo);
|
||||
lines = lineNumber.Lines;
|
||||
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName);
|
||||
h1 = fileNameWithoutExtension.ToLower().Replace("%20", "-").Replace(' ', '-');
|
||||
frontMatterYaml = GetFromMatterYaml(lines, lineNumber);
|
||||
@ -690,7 +706,7 @@ internal static partial class HelperMarkdown
|
||||
continue;
|
||||
type = appSettings.DefaultNoteType;
|
||||
File.WriteAllLines(fileInfo.FullName, ["---", $"type: {type}\"", "---", string.Empty, $"# {h1}"]);
|
||||
lines = File.ReadAllLines(fileInfo.FullName).ToList();
|
||||
lines = File.ReadAllLines(fileInfo.FullName).AsReadOnly();
|
||||
}
|
||||
isKanbanMarkdown = fileInfo.Name.EndsWith(".knb.md");
|
||||
isKanbanIndex = fileNameWithoutExtension == "index" && type.StartsWith("kanb", StringComparison.OrdinalIgnoreCase);
|
||||
@ -1042,7 +1058,7 @@ internal static partial class HelperMarkdown
|
||||
}
|
||||
}
|
||||
|
||||
private static (string type, string h1) GetTypeAndH1(AppSettings appSettings, string h1, List<string> lines, LineNumber lineNumber)
|
||||
private static (string type, string h1) GetTypeAndH1(AppSettings appSettings, string h1, ReadOnlyCollection<string> lines, LineNumber lineNumber)
|
||||
{
|
||||
string type = lineNumber.Type is null ? appSettings.DefaultNoteType : lines[lineNumber.Type.Value][5..].Trim().Trim('"');
|
||||
string h1FromFile = lineNumber.H1 is null ? h1 : lines[lineNumber.H1.Value][2..];
|
||||
@ -1080,7 +1096,7 @@ internal static partial class HelperMarkdown
|
||||
createdLine = $"created: {creationDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}";
|
||||
updatedLine = $"updated: {markdownFile.LastWriteDateTime.ToUniversalTime():yyyy-MM-ddTHH:mm:ss.fffZ}";
|
||||
if (markdownFile.IsKanbanIndex)
|
||||
HelperKanbanMetadata.SetMetadata(markdownFile.Directory, new(lines), markdownFile.LineNumber, gitOthersModifiedAndDeletedExcludingStandardFiles);
|
||||
HelperKanbanMetadata.SetMetadata(markdownFile.Directory, markdownFile.LineNumber, gitOthersModifiedAndDeletedExcludingStandardFiles);
|
||||
if (markdownFile.LineNumber.FrontMatterYamlEnd is null)
|
||||
{
|
||||
if (markdownFile.LineNumber.H1 is not null)
|
||||
|
@ -1,10 +1,14 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace File_Folder_Helper.Models;
|
||||
|
||||
internal record LineNumber(int? Created,
|
||||
int? Completed,
|
||||
int? H1,
|
||||
int? FrontMatterYamlEnd,
|
||||
ReadOnlyCollection<string> Lines,
|
||||
int? Progress,
|
||||
int? Status,
|
||||
int? Type,
|
||||
int? Updated);
|
||||
|
@ -1,12 +1,15 @@
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace File_Folder_Helper.Models;
|
||||
|
||||
internal record NginxFileSystem(string Name,
|
||||
string Type,
|
||||
string MTime,
|
||||
float Size)
|
||||
internal record NginxFileSystem([property: JsonPropertyName("name")] string Name,
|
||||
DateTime? LastModified,
|
||||
[property: JsonPropertyName("mtime")] string MTime,
|
||||
Uri? URI,
|
||||
[property: JsonPropertyName("type")] string Type,
|
||||
[property: JsonPropertyName("size")] float? Length)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
@ -15,6 +18,25 @@ internal record NginxFileSystem(string Name,
|
||||
return result;
|
||||
}
|
||||
|
||||
public static NginxFileSystem Get(string format, TimeZoneInfo timeZoneInfo, string name, string mTime, Uri uri, string type, float? size)
|
||||
{
|
||||
NginxFileSystem result;
|
||||
DateTime dateTime;
|
||||
DateTime? nullableDateTime;
|
||||
if (mTime.Length != format.Length + 4 || !DateTime.TryParseExact(mTime[..format.Length], format, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
|
||||
nullableDateTime = null;
|
||||
else
|
||||
nullableDateTime = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dateTime, mTime[(format.Length + 1)..], timeZoneInfo.Id);
|
||||
result = new(name, nullableDateTime, mTime, uri, type, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string GetFormat() =>
|
||||
"ddd, dd MMM yyyy HH:mm:ss";
|
||||
|
||||
public static NginxFileSystem Get(string format, TimeZoneInfo timeZoneInfo, Uri uri, NginxFileSystem nginxFileSystem) =>
|
||||
Get(format, timeZoneInfo, nginxFileSystem.Name, nginxFileSystem.MTime, uri, nginxFileSystem.Type, nginxFileSystem.Length);
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
|
@ -14,6 +14,8 @@
|
||||
// console.log("ticks: " + ticks);
|
||||
// var dateText = ticks + " - " + date.toString();
|
||||
// console.log("dateText: " + dateText);
|
||||
// DateTime utcMeDateTime = new(1980, 1, 17, 0, 0, 0, DateTimeKind.Utc);
|
||||
// long meTotalSeconds = (long)Math.Floor(fileInfo.LastWriteTime.ToUniversalTime().Subtract(utcMeDateTime).TotalSeconds);
|
||||
let date = new Date();
|
||||
let timezoneOffset = date.getTimezoneOffset();
|
||||
let seconds = date.getTime().valueOf() + timezoneOffset;
|
||||
@ -27,5 +29,5 @@ console.log("end");
|
||||
// dateText: 638665132483790000 - Wed Nov 06 2024 10:55:58 GMT-0700 (Mountain Standard Time)
|
||||
// Now - To: 638665132334594771
|
||||
// 638665135325760000
|
||||
// 638665136814890000
|
||||
// 638665136814890000
|
||||
// utc1970DateTime: 621355968000000000
|
Loading…
x
Reference in New Issue
Block a user