using Microsoft.Extensions.Logging; using System.Globalization; namespace File_Folder_Helper.ADO2025.PI7; internal static partial class Helper20250926 { internal static void RenameThenFindFirstAndLast(ILogger logger, List args) { logger.LogInformation(args[0]); logger.LogInformation(args[1]); logger.LogInformation(args[2]); logger.LogInformation(args[3]); logger.LogInformation(args[4]); logger.LogInformation(args[5]); const char star = '*'; const char underscore = '_'; string dateFormat = args[4]; string searchString = args[3]; int seconds = int.Parse(args[5]); string[] searchPatterns = args[2].Split('~'); string key = searchString.ToLower().Split(' ')[0]; string[] sourceDirectories = Path.GetFullPath(args[0]).Split('~'); if (args.Count == 999) { Redo(logger, star, underscore, dateFormat, searchString, key, searchPatterns, sourceDirectories); } Rename(logger, star, underscore, dateFormat, searchString, key, searchPatterns, sourceDirectories); if (seconds > 0) { FindFirstAndLast(logger, star, underscore, dateFormat, searchString, seconds, searchPatterns, sourceDirectories); } } private static void Redo(ILogger logger, char star, char underscore, string dateFormat, string searchString, string key, string[] searchPatterns, string[] sourceDirectories) { bool found; string check; string[] files; string[] lines; string fileName; string checkFile; string checkPath; DateTime lastDate; DateTime firstDate; string directoryName; string[] directories; string? directoryPath; foreach (string sourceDirectory in sourceDirectories) { directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string searchPattern in searchPatterns) { if (searchPattern.Contains(underscore) || !searchPattern.Contains(star)) { throw new Exception($"{nameof(searchPattern)} must not contain {underscore} and must contain {star}"); } foreach (string directory in directories) { directoryName = Path.GetFileName(directory); files = Directory.GetFiles(directory, searchPattern, SearchOption.AllDirectories); logger.LogInformation("With search pattern '{SearchPattern}' found {files} file(s)", searchPattern, files.Length); foreach (string file in files) { found = false; lastDate = DateTime.MinValue; firstDate = DateTime.MinValue; fileName = Path.GetFileName(file); directoryPath = Path.GetDirectoryName(file); if (string.IsNullOrEmpty(directoryPath)) { logger.LogInformation("skipped (empty) '{directoryPath}'", directoryPath); continue; } try { lines = File.ReadAllLines(file); foreach (string line in lines) { if (!found && line.Contains(searchString)) { found = true; } if (line.Length < dateFormat.Length) { continue; } check = line[..dateFormat.Length]; if (!DateTime.TryParseExact(check, dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime logDate)) { continue; } if (firstDate == DateTime.MinValue) { firstDate = logDate; } lastDate = logDate; } if (!found) { checkFile = $"{directoryName}_{firstDate:yyyy-MM-dd_HH-mm-ss}---{lastDate:yyyy-MM-dd_HH-mm-ss}.log"; } else { checkFile = $"{directoryName}_{firstDate:yyyy-MM-dd_HH-mm-ss}---{lastDate:yyyy-MM-dd_HH-mm-ss}~~~{key}.log"; } checkPath = Path.Combine(directoryPath, checkFile); if (checkFile == fileName) { logger.LogDebug("skipped (match) '{checkFile}'", checkFile); } else if (File.Exists(checkPath)) { logger.LogInformation("skipped (exists) '{checkPath}'", checkPath); } else { logger.LogInformation("rename to '{checkPath}'", checkPath); File.Move(file, checkPath); } } catch { logger.LogWarning("skipped (error) {TotalHours} hour(s) -> '{FileInfo}'", file, (DateTime.Now - new FileInfo(file).LastWriteTime).TotalHours); } } } } } } private static void Rename(ILogger logger, char star, char underscore, string dateFormat, string searchString, string key, string[] searchPatterns, string[] sourceDirectories) { bool found; string check; string[] files; string[] lines; string checkFile; string checkPath; DateTime lastDate; string[] segments; DateTime firstDate; string[] segmentsB; string directoryName; string[] directories; string destinationDirectory; FileInfo[] fileInfoCollection; foreach (string sourceDirectory in sourceDirectories) { directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string searchPattern in searchPatterns) { if (!searchPattern.Contains(underscore) || !searchPattern.Contains(star)) { throw new Exception($"{nameof(searchPattern)} must contain {underscore} and {star}"); } segments = searchPattern.Split(star); segmentsB = segments[0].Split(underscore); foreach (string directory in directories) { directoryName = Path.GetFileName(directory); destinationDirectory = Path.Combine(directory, segmentsB[0], segmentsB[1]); if (!Directory.Exists(destinationDirectory)) { _ = Directory.CreateDirectory(destinationDirectory); } files = Directory.GetFiles(directory, searchPattern, SearchOption.TopDirectoryOnly); logger.LogInformation("With search pattern '{SearchPattern}' found {files} file(s)", searchPattern, files.Length); fileInfoCollection = files.Select(f => new FileInfo(f)).ToArray(); foreach (FileInfo fileInfo in fileInfoCollection.OrderBy(f => f.LastWriteTime)) { if (fileInfo.Length == 0) { logger.LogDebug("skipped (0k) '{FileInfo}'", fileInfo.FullName); continue; } else if (fileInfo.LastWriteTime > DateTime.Now.AddMinutes(-1)) { logger.LogDebug("skipped (too new) '{FileInfo}'", fileInfo.FullName); continue; } found = false; lastDate = DateTime.MinValue; firstDate = DateTime.MinValue; try { lines = File.ReadAllLines(fileInfo.FullName); foreach (string line in lines) { if (!found && line.Contains(searchString)) { found = true; } if (line.Length < dateFormat.Length) { continue; } check = line[..dateFormat.Length]; if (!DateTime.TryParseExact(check, dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime logDate)) { continue; } if (firstDate == DateTime.MinValue) { firstDate = logDate; } lastDate = logDate; } if (!found) { checkFile = $"{directoryName}_{firstDate:yyyy-MM-dd_HH-mm-ss}---{lastDate:yyyy-MM-dd_HH-mm-ss}.log"; } else { checkFile = $"{directoryName}_{firstDate:yyyy-MM-dd_HH-mm-ss}---{lastDate:yyyy-MM-dd_HH-mm-ss}~~~{key}.log"; } checkPath = Path.Combine(destinationDirectory, checkFile); if (checkFile == fileInfo.Name) { logger.LogDebug("skipped (match) '{checkFile}'", checkFile); } else if (File.Exists(checkPath)) { logger.LogInformation("skipped (exists) '{checkPath}'", checkPath); } else { logger.LogInformation("rename to '{checkPath}'", checkPath); File.Move(fileInfo.FullName, checkPath); } } catch { logger.LogWarning("skipped (error) {TotalHours} hour(s) -> '{FileInfo}'", (DateTime.Now - fileInfo.LastWriteTime).TotalHours, fileInfo.FullName); } } Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, directory); } } } } private static void FindFirstAndLast(ILogger logger, char star, char underscore, string dateFormat, string searchString, int seconds, string[] searchPatterns, string[] sourceDirectories) { string line; string check; string[] files; string[] lines; DateTime lastDate; double totalHours; string[] segments; TimeSpan timeSpan; DateTime firstDate; string[] segmentsB; string directoryName; string[] directories; string searchPatternB; string destinationDirectory; FileInfo[] fileInfoCollection; DateTime logDate = DateTime.MinValue; foreach (string sourceDirectory in sourceDirectories) { directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string searchPattern in searchPatterns) { if (!searchPattern.Contains(underscore) || !searchPattern.Contains(star)) { throw new Exception($"{nameof(searchPattern)} must contain {underscore} and {star}"); } segments = searchPattern.TrimEnd(star).Split(star); segmentsB = segments[0].Split(underscore); foreach (string directory in directories) { directoryName = Path.GetFileName(directory); destinationDirectory = Path.Combine(directory, segmentsB[0], segmentsB[1]); if (!Directory.Exists(destinationDirectory)) { logger.LogInformation("skipped (doesn't exist) '{destinationDirectory}'", destinationDirectory); continue; } searchPatternB = $"*{segments[^1]}*"; files = Directory.GetFiles(destinationDirectory, searchPatternB, SearchOption.TopDirectoryOnly); logger.LogInformation("For {destinationDirectory} with search pattern '{SearchPatternB}' found {files} file(s)", destinationDirectory, searchPatternB, files.Length); fileInfoCollection = files.Select(f => new FileInfo(f)).ToArray(); foreach (FileInfo fileInfo in fileInfoCollection.OrderBy(f => f.LastWriteTime)) { if (fileInfo.Length == 0) { logger.LogDebug("skipped (0k) '{FileInfo}'", fileInfo.FullName); continue; } else if (fileInfo.LastWriteTime > DateTime.Now.AddMinutes(-1)) { logger.LogDebug("skipped (too new) '{FileInfo}'", fileInfo.FullName); continue; } lastDate = DateTime.MinValue; firstDate = DateTime.MinValue; try { lines = File.ReadAllLines(fileInfo.FullName); for (int i = 0; i < lines.Length; i++) { line = lines[i]; if (!line.Contains(searchString) || lines[i - 1].Length < dateFormat.Length) { continue; } check = lines[i - 1][..dateFormat.Length]; if (!DateTime.TryParseExact(check, dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out logDate)) { continue; } timeSpan = logDate - lastDate; if (firstDate == DateTime.MinValue || timeSpan.TotalSeconds > seconds) { if (firstDate != DateTime.MinValue) { totalHours = Math.Round((lastDate - firstDate).TotalHours, 2); logger.LogInformation("Ran for {totalHours} hour(s) {logDate} - {firstDate} {file}", totalHours, logDate.ToString("HH:mm:ss"), firstDate.ToString("HH:mm:ss"), fileInfo.FullName[directory.Length..]); } firstDate = logDate; } lastDate = logDate; } if (firstDate != DateTime.MinValue && logDate != DateTime.MinValue) { totalHours = Math.Round((lastDate - firstDate).TotalHours, 2); logger.LogInformation("Ran for {totalHours} hour(s) {logDate} - {firstDate} {file}", totalHours, logDate.ToString("HH:mm:ss"), firstDate.ToString("HH:mm:ss"), fileInfo.FullName[directory.Length..]); } } catch { logger.LogWarning("skipped (error) {TotalHours} hour(s) -> '{FileInfo}'", (DateTime.Now - fileInfo.LastWriteTime).TotalHours, fileInfo.FullName); } } Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, directory); } } } } }