diff --git a/Compare/Compare.cs b/Compare/Compare.cs index 9bacc79..18e0243 100644 --- a/Compare/Compare.cs +++ b/Compare/Compare.cs @@ -160,7 +160,7 @@ public class Compare } if (_IsEnvironment.Development && propertyConfiguration.PopulatePropertyId) throw new Exception("Copy keyValuePairs-####.json file"); - (int j, int f, int t, Shared.Models.Container[] containers) = Shared.Models.Stateless.Methods.IContainer.GetContainers(propertyConfiguration, propertyLogic); + (int j, int f, int t, Shared.Models.Container[] containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(propertyConfiguration, propertyLogic); if (propertyLogic.ExceptionsDirectories.Any()) throw new Exception(); if (propertyConfiguration.PopulatePropertyId && Shared.Models.Stateless.Methods.IProperty.Any(containers)) diff --git a/Container/.vscode/format-report.json b/Container/.vscode/format-report.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/Container/.vscode/format-report.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/Container/.vscode/settings.json b/Container/.vscode/settings.json new file mode 100644 index 0000000..0852522 --- /dev/null +++ b/Container/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "cSpell.words": [ + "dlib", + "Exif", + "nosj", + "Serilog" + ] +} \ No newline at end of file diff --git a/Container/Container.csproj b/Container/Container.csproj new file mode 100644 index 0000000..bc45f6b --- /dev/null +++ b/Container/Container.csproj @@ -0,0 +1,51 @@ + + + enable + enable + library + win-x64;linux-x64 + net9.0 + + + Phares.View.by.Distance.Container + false + 9.0.100.1 + Mike Phares + Phares + true + snupkg + + + + true + + + true + + + true + + + + Windows + + + OSX + + + Linux + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Shared/Models/Container.cs b/Container/Models/Container.cs similarity index 80% rename from Shared/Models/Container.cs rename to Container/Models/Container.cs index 8af98c8..483d840 100644 --- a/Shared/Models/Container.cs +++ b/Container/Models/Container.cs @@ -1,7 +1,8 @@ using System.Collections.ObjectModel; using System.Text.Json; +using View_by_Distance.Shared.Models; -namespace View_by_Distance.Shared.Models; +namespace View_by_Distance.Container.Models; public record Container(string SourceDirectory, ReadOnlyCollection Items) { diff --git a/Container/Models/Stateless/Methods/Container.cs b/Container/Models/Stateless/Methods/Container.cs new file mode 100644 index 0000000..e274538 --- /dev/null +++ b/Container/Models/Stateless/Methods/Container.cs @@ -0,0 +1,664 @@ +using System.Collections.ObjectModel; +using System.Text.Json; +using View_by_Distance.Shared.Models; +using View_by_Distance.Shared.Models.Properties; +using View_by_Distance.Shared.Models.Stateless.Methods; + +namespace View_by_Distance.Container.Models.Stateless.Methods; + +internal abstract class Container +{ + + private record FilePair(bool IsUnique, List Collection, FilePath FilePath, Item Item) { } + + internal static DateTime[] GetContainerDateTimes(ReadOnlyCollection items) + { + DateTime[] results; + long containerMinimumTicks = (from l in items select l.FilePath.LastWriteTicks).Min(); + long containerMaximumTicks = (from l in items select l.FilePath.LastWriteTicks).Max(); + results = [new(containerMinimumTicks), new(containerMaximumTicks)]; + return results; + } + + internal static ReadOnlyCollection GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) + { + List results = []; + foreach (Item item in container.Items) + { + if (!item.IsValidImageFormatExtension || propertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered)) + continue; + results.Add(item); + } + return container.Items.Count == results.Count ? container.Items : results.AsReadOnly(); + } + + private static IReadOnlyDictionary> GetFilesKeyValuePairs(ReadOnlyCollection> filePathsCollection) + { + Dictionary> results = []; + List? collection; + string fileNameWithoutExtensionMinusOne; + foreach (ReadOnlyCollection filePaths in filePathsCollection) + { + foreach (FilePath filePath in filePaths) + { + fileNameWithoutExtensionMinusOne = filePath.NameWithoutExtension[..^1]; + if (!results.TryGetValue(fileNameWithoutExtensionMinusOne, out collection)) + { + results.Add(fileNameWithoutExtensionMinusOne, []); + if (!results.TryGetValue(fileNameWithoutExtensionMinusOne, out collection)) + throw new Exception(); + } + collection.Add(filePath.FullName); + } + } + return results; + } + + private static IReadOnlyDictionary> GetFilesKeyValuePairs(ReadOnlyCollection filesCollection) + { + Dictionary> results = []; + string fileNameWithoutExtension; + string fileNameWithoutExtensionSecond; + string fileNameWithoutExtensionSecondMinusOne; + List? collection; + foreach (string[] files in filesCollection) + { + foreach (string file in files) + { + fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); + fileNameWithoutExtensionSecond = Path.GetFileNameWithoutExtension(fileNameWithoutExtension); + if (string.IsNullOrEmpty(fileNameWithoutExtensionSecond) || fileNameWithoutExtensionSecond == fileNameWithoutExtension) + continue; + fileNameWithoutExtensionSecondMinusOne = fileNameWithoutExtensionSecond[..^1]; + if (!results.TryGetValue(fileNameWithoutExtensionSecondMinusOne, out collection)) + { + results.Add(fileNameWithoutExtensionSecondMinusOne, []); + if (!results.TryGetValue(fileNameWithoutExtensionSecondMinusOne, out collection)) + throw new Exception(); + } + collection.Add(file); + } + } + return results; + } + + private static bool AnyMoved(IReadOnlyDictionary> fileNamesToFiles, string extension, List renameCollection, string[] files) + { + bool result = false; + string checkFile; + string directory; + string fileNameWith; + string checkDirectory; + string directoryName; + List? collection; + string fileNameWithoutExtension; + List directoryNames = []; + string fileNameWithoutExtensionSecond; + string fileNameWithoutExtensionSecondMinusOne; + foreach (string file in files) + { + if (!file.EndsWith(extension)) + throw new Exception(); + fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); + fileNameWithoutExtensionSecond = Path.GetFileNameWithoutExtension(fileNameWithoutExtension); + if (string.IsNullOrEmpty(fileNameWithoutExtensionSecond) || fileNameWithoutExtensionSecond == fileNameWithoutExtension) + continue; + fileNameWithoutExtensionSecondMinusOne = fileNameWithoutExtensionSecond[..^1]; + if (!fileNamesToFiles.TryGetValue(fileNameWithoutExtensionSecondMinusOne, out collection)) + renameCollection.Add(file); + else + { + directoryNames.Clear(); + directoryName = Path.GetFileName(Path.GetDirectoryName(file)) ?? throw new Exception(); + foreach (string f in collection) + directoryNames.Add(Path.GetFileName(Path.GetDirectoryName(f)) ?? throw new Exception()); + if (directoryNames.Count == 0 || directoryNames.Distinct().Count() != 1) + continue; + if (directoryName != directoryNames[0]) + { + directory = Path.GetDirectoryName(Path.GetDirectoryName(file)) ?? throw new Exception(); + checkDirectory = Path.Combine(directory, directoryNames[0]); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + fileNameWith = collection.Count > 1 ? Path.GetFileName(file) : $"{Path.GetFileName(collection[0])}{extension}"; + checkFile = Path.Combine(checkDirectory, fileNameWith); + if (!result) + result = true; + if (!File.Exists(checkFile)) + File.Move(file, checkFile); + else + { + if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime) + File.Delete(file); + else + File.Move(file, checkFile, true); + } + } + } + } + return result; + } + + private static int LookForAbandoned(ReadOnlyCollection jsonFilesCollection, IReadOnlyDictionary> fileNamesToFiles, string extension) + { + int result; + bool check; + bool moved = false; + List renameCollection = []; + foreach (string[] files in jsonFilesCollection) + { + if (files.Length == 0) + continue; + check = AnyMoved(fileNamesToFiles, extension, renameCollection, files); + if (!moved && check) + moved = true; + } + if (renameCollection.Count > 0) + IDirectory.MoveFiles(renameCollection, "{}", "{abd}"); + result = renameCollection.Count; + if (moved && result == 0) + result = 1; + return result; + } + + private static List GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles) + { + List? results = null; + int renamed; + const bool useCeilingAverage = true; + ReadOnlyCollection? jsonFilesCollection = null; + IReadOnlyDictionary>? compareFileNamesToFiles = null; + for (int i = 0; i < short.MaxValue; i++) + { + renamed = 0; + jsonFilesCollection = IDirectory.GetFilesCollection(jsonGroupDirectory, directorySearchFilter, extension, useCeilingAverage); + compareFileNamesToFiles = GetFilesKeyValuePairs(jsonFilesCollection); + renamed += LookForAbandoned(jsonFilesCollection, fileNamesToFiles, extension); + results = IDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles); + renamed += IDirectory.MaybeMove(propertyConfiguration, results, jsonGroupDirectory, extension); + if (renamed == 0) + break; + if (i > 10) + throw new NotImplementedException(); + } + if (results is null || jsonFilesCollection is null || compareFileNamesToFiles is null) + throw new NullReferenceException(nameof(results)); + return results; + } + + private static Property? GetProperty(Shared.Models.FilePair filePair) + { + Property? result; + if (filePair.Match is null) + result = null; + else + { + string json = File.ReadAllText(filePair.Match); + if (string.IsNullOrEmpty(json)) + result = null; + else + result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property); + } + return result; + } + + private static ExifDirectory? GetExifDirectory(Shared.Models.FilePair filePair) + { + ExifDirectory? result; + if (filePair.Match is null) + result = null; + else + { + string json = File.ReadAllText(filePair.Match); + if (string.IsNullOrEmpty(json)) + result = null; + else + result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + } + return result; + } + + private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, string fullFileName) + { + string[] segments = directory.Split(cei.Combined); + string? checkDirectory = segments.Length == 1 ? + Path.Combine(segments[0], $"{cei.Combined[2..]}") : + segments.Length == 2 ? + $"{segments[0]}{cei.Combined[2..]}{segments[1]}" : + null; + if (checkDirectory is not null && Directory.Exists(checkDirectory)) + { + string checkFile = Path.Combine(checkDirectory, fileName); + if (File.Exists(checkFile)) + File.Move(checkFile, fullFileName); + } + } + + private static void RenameFile(string extension, Shared.Models.FilePair filePair, FilePath filePath, char change, ReadOnlyCollection filePaths) + { + string checkFile; + if (filePath.DirectoryFullPath.Contains("Results") && filePath.DirectoryFullPath.Contains("Resize")) + File.Delete(filePath.FullName); + if (!string.IsNullOrEmpty(filePair.Match)) + { + string directory = Path.GetDirectoryName(filePair.Match) ?? throw new Exception(); + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePair.Match); + string fileNameWithoutExtensionSecond = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(filePair.Match)); + string extensionSecond = Path.GetExtension(fileNameWithoutExtension); + checkFile = Path.Combine(directory, $"{fileNameWithoutExtensionSecond[..^1]}{change}{extensionSecond}{extension}"); + if (!File.Exists(checkFile)) + File.Move(filePair.Match, checkFile); + } + foreach (FilePath f in filePaths) + { + checkFile = Path.Combine(f.DirectoryFullPath, $"{f.NameWithoutExtension[..^1]}{change}{f.ExtensionLowered}"); + if (File.Exists(checkFile)) + continue; + File.Move(f.FullName, checkFile); + } + } + + private static void ParallelFor(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, int rootDirectoryLength, Shared.Models.FilePair filePair, List results) + { + dlibDotNet?.Tick(); + bool abandoned = false; + FileHolder sourceDirectoryFileHolder; + Property? property = GetProperty(filePair); + FileHolder fileHolder = IFileHolder.Get(filePair.Path); + ExifDirectory? exifDirectory = GetExifDirectory(filePair); + FilePath filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); + bool? fileSizeChanged = property is not null ? property.FileSize != filePath.Length : null; + bool isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(filePath.ExtensionLowered); + bool? shouldIgnore = property is null || property.Keywords is null ? null : propertyConfiguration.IgnoreRulesKeyWords.Any(l => property.Keywords.Contains(l)); + bool? isArchive = filePath.Id is null || splatNineIdentifiers is null ? null : splatNineIdentifiers.TryGetValue(filePath.Id.Value, out Identifier? identifier); + if (property is not null && filePath.Id is not null && filePath.HasIgnoreKeyword is not null && filePath.HasDateTimeOriginal is not null) + { + char? change; + ReadOnlyCollection? filePaths = null; + char hasIgnoreKeyword = IId.GetHasIgnoreKeyword(filePath).ToString()[0]; + char hasDateTimeOriginal = IId.GetHasDateTimeOriginal(filePath).ToString()[0]; + char missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(filePath).ToString()[0]; + if (shouldIgnore is not null && shouldIgnore.Value) + { + if (filePath.NameWithoutExtension[^1] == hasIgnoreKeyword) + change = null; + else + { + change = hasIgnoreKeyword; + if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePath.Id.Value, out filePaths) || filePaths is null) + throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>"); + } + } + else if ((shouldIgnore is null || !shouldIgnore.Value) && property.DateTimeOriginal is null) + { + if (filePath.NameWithoutExtension[^1] == missingDateTimeOriginal) + change = null; + else + { + change = missingDateTimeOriginal; + if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePath.Id.Value, out filePaths) || filePaths is null) + throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>"); + } + } + else if (filePath.NameWithoutExtension[^1] != hasDateTimeOriginal) + { + change = hasDateTimeOriginal; + if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePath.Id.Value, out filePaths) || filePaths is null) + throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>"); + } + else + change = null; + if (filePaths is not null && change is not null) + RenameFile(extension, filePair, filePath, change.Value, filePaths); + } + string relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(filePair.Path, rootDirectoryLength, forceExtensionToLower: true); + bool? lastWriteTimeChanged = property is not null ? propertyConfiguration.PropertiesChangedForProperty || property.LastWriteTime.Ticks != filePath.LastWriteTicks : null; + if (filePair.Match is not null) + sourceDirectoryFileHolder = IFileHolder.Get(filePair.Match); + else if (!filePair.IsUnique) + sourceDirectoryFileHolder = IFileHolder.Get(Path.GetFullPath(string.Concat(jsonGroupDirectory, relativePath, extension))); + else + { + string fileName = Path.GetFileName(filePair.Path); + CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); + string directory = Path.Combine(jsonGroupDirectory, cei.Combined); + string jsonFileName = $"{fileName}{extension}"; + string fullFileName = Path.Combine(directory, jsonFileName); + MoveIf(jsonFileName, cei, directory, fullFileName); + sourceDirectoryFileHolder = IFileHolder.Get(fullFileName); + } + if (sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && filePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks) + { + File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePath.LastWriteTicks)); + File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value); + } + Item item = Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, property, abandoned, fileSizeChanged, lastWriteTimeChanged); + lock (results) + results.Add(new(filePair.IsUnique, filePair.Collection, filePath, item)); + } + + private static List GetFilePairs(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, string directorySearchFilter) + { + List results = []; + const string extension = ".json"; + List filePairs; + string jsonGroupDirectory = aPropertySingletonDirectory; + int maxDegreeOfParallelism = Environment.ProcessorCount; + int filesCollectionDirectoryLength = filesCollectionDirectory.Length; + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; + IReadOnlyDictionary> fileNamesToFiles = GetFilesKeyValuePairs(filePathsCollection); + filePairs = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection, fileNamesToFiles); + _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => + ParallelFor(dlibDotNet, propertyConfiguration, jsonGroupDirectory, extension, keyValuePairs, splatNineIdentifiers, filesCollectionDirectoryLength, filePairs[i], results)); + return results; + } + + private static (int, Models.Container[]) GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, string directorySearchFilter) + { + List results = []; + string directory; + List? items; + Models.Container container; + List directories = []; + Dictionary> directoryToItems = []; + foreach (ReadOnlyCollection filePaths in filePathsCollection) + { + if (filePaths.Count == 0) + continue; + directory = filePaths[0].DirectoryFullPath; + if (directory is null) + continue; + if (!directories.Contains(directory)) + directories.Add(directory); + if (!directoryToItems.TryGetValue(directory, out items)) + { + directoryToItems.Add(directory, []); + if (!directoryToItems.TryGetValue(directory, out items)) + throw new Exception(); + } + } + (string aResultsFullGroupDirectory, _) = dlibDotNet.GetResultsFullGroupDirectories(); + string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); + if (!Directory.Exists(aPropertySingletonDirectory)) + _ = Directory.CreateDirectory(aPropertySingletonDirectory); + List filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, directorySearchFilter); + foreach (FilePair filePair in filePairs) + { + if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items)) + { + directoryToItems.Add(filePair.FilePath.DirectoryFullPath, []); + if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items)) + throw new Exception(); + } + items.Add(filePair.Item); + } + foreach (KeyValuePair> keyValuePair in directoryToItems) + { + if (keyValuePair.Value.Count == 0) + continue; + container = new(keyValuePair.Key, new(keyValuePair.Value)); + results.Add(container); + } + return (filePairs.Count, results.ToArray()); + } + + private static void AnyMovedFace(IReadOnlyDictionary> fileNamesToFiles, List directories) + { + bool result = false; + string checkFile; + string subDirectory; + string directoryName; + string checkDirectory; + List? collection; + string directoryNameWith; + string directoryNameMinusOne; + List directoryNames = []; + foreach (string directory in directories) + { + directoryNameMinusOne = Path.GetFileName(directory)[..^1]; + if (!fileNamesToFiles.TryGetValue(directoryNameMinusOne, out collection)) + throw new Exception(); + directoryNames.Clear(); + foreach (string f in collection) + directoryNames.Add(Path.GetFileName(Path.GetDirectoryName(f)) ?? throw new Exception()); + if (directoryNames.Count == 0 || directoryNames.Distinct().Count() != 1) + continue; + directoryName = Path.GetFileName(Path.GetDirectoryName(directory)) ?? throw new Exception(); + if (directoryName != directoryNames[0]) + { + subDirectory = Path.GetDirectoryName(Path.GetDirectoryName(directory)) ?? throw new Exception(); + checkDirectory = Path.Combine(subDirectory, directoryNames[0]); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + directoryNameWith = collection.Count > 1 ? directoryName : $"{Path.GetFileNameWithoutExtension(collection[0])}"; + checkFile = Path.Combine(checkDirectory, directoryNameWith); + if (!result) + result = true; + if (!Directory.Exists(checkFile)) + Directory.Move(directory, checkFile); + else + { + if (new DirectoryInfo(directory).LastWriteTime > new DirectoryInfo(checkFile).LastWriteTime) + Directory.Delete(directory, recursive: true); + else + { + Directory.Delete(checkFile, recursive: true); + Directory.Move(directory, checkFile); + } + } + } + } + } + + private static void AnyMovedFace(string extension, string hiddenExtension, IReadOnlyDictionary> fileNamesToFiles, string jsonGroupDirectory) + { + string directory; + string checkFile; + string directoryName; + List files = []; + string[] fileNameSegments; + List directories = []; + files.AddRange(Directory.GetFiles(jsonGroupDirectory, $"*{extension}", SearchOption.AllDirectories)); + files.AddRange(Directory.GetFiles(jsonGroupDirectory, $"*{hiddenExtension}", SearchOption.AllDirectories)); + foreach (string file in files) + { + directory = Path.GetDirectoryName(file) ?? throw new Exception(); + if (!directories.Contains(directory)) + directories.Add(directory); + directoryName = Path.GetFileName(directory); + fileNameSegments = Path.GetFileName(file).Split('.'); + if (fileNameSegments[0] != directoryName) + { + fileNameSegments[0] = string.Empty; + checkFile = Path.Combine(directory, $"{directoryName}{string.Join('.', fileNameSegments)}"); + if (!File.Exists(checkFile)) + File.Move(file, checkFile); + else + { + if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime) + File.Delete(file); + else + File.Move(file, checkFile, true); + } + } + } + if (directories.Count > 0) + AnyMovedFace(fileNamesToFiles, directories); + } + + private static void AnyMovedDistance(string extension, IReadOnlyDictionary> fileNamesToFiles, string jsonGroupDirectory) + { + bool result = false; + string fileName; + string checkFile; + string directory; + List? collection; + string[] fileNameSegments; + List fileNames = []; + string fileNameSegmentsZeroMinusOne; + string[] files = Directory.GetFiles(jsonGroupDirectory, $"*{extension}", SearchOption.AllDirectories); + foreach (string file in files) + { + fileName = Path.GetFileName(file); + fileNameSegments = fileName.Split('.'); + fileNameSegmentsZeroMinusOne = fileNameSegments[0][..^1]; + if (!fileNamesToFiles.TryGetValue(fileNameSegmentsZeroMinusOne, out collection)) + continue; + fileNames.Clear(); + foreach (string f in collection) + fileNames.Add(Path.GetFileNameWithoutExtension(f) ?? throw new Exception()); + if (fileNames.Count == 0 || fileNames.Distinct().Count() != 1) + continue; + if (fileNameSegments[0] != fileNames[0]) + { + fileNameSegments[0] = string.Empty; + directory = Path.GetDirectoryName(file) ?? throw new Exception(); + checkFile = Path.Combine(directory, $"{fileNames[0]}{string.Join('.', fileNameSegments)}"); + if (!result) + result = true; + if (!File.Exists(checkFile)) + File.Move(file, checkFile); + else + { + if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime) + File.Delete(file); + else + File.Move(file, checkFile, true); + } + } + } + } + + private static void DoGetFilePairsForRemaining(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, ReadOnlyCollection> filePathsCollection, string directorySearchFilter) + { + const string extension = ".json"; + (_, string bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); + IReadOnlyDictionary> fileNamesToFiles = GetFilesKeyValuePairs(filePathsCollection); + string bMetaSingletonDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); + if (!Directory.Exists(bMetaSingletonDirectory)) + _ = Directory.CreateDirectory(bMetaSingletonDirectory); + _ = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, bMetaSingletonDirectory, filePathsCollection, fileNamesToFiles); + (string cResultsFullGroupDirectory, _, string dResultsFullGroupDirectory, _) = dlibDotNet.GetResultsFullGroupDirectories("Original"); + string cResizeSingletonDirectory = Path.Combine(cResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); + if (!Directory.Exists(cResizeSingletonDirectory)) + _ = Directory.CreateDirectory(cResizeSingletonDirectory); + _ = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, cResizeSingletonDirectory, filePathsCollection, fileNamesToFiles); + string dFaceCollectionDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultCollection); + if (!Directory.Exists(dFaceCollectionDirectory)) + _ = Directory.CreateDirectory(dFaceCollectionDirectory); + _ = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, dFaceCollectionDirectory, filePathsCollection, fileNamesToFiles); + string dFaceContentDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultContent); + if (!Directory.Exists(dFaceContentDirectory)) + _ = Directory.CreateDirectory(dFaceContentDirectory); + AnyMovedFace(facesFileNameExtension, facesHiddenFileNameExtension, fileNamesToFiles, dFaceContentDirectory); + AnyMovedDistance(facesFileNameExtension, fileNamesToFiles, eDistanceContentDirectory); + } + + internal static ReadOnlyCollection GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection) + { + Models.Container[] results; + const string directorySearchFilter = "*"; + (_, results) = GetContainers(dlibDotNet, propertyConfiguration, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, directorySearchFilter); + if (keyValuePairs is not null) + DoGetFilePairsForRemaining(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filePathsCollection, directorySearchFilter); + return results.AsReadOnly(); + } + + private static IDlibDotNet GetDlibDotNet() => + throw new NotImplementedException(); + + internal static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string _) + { + int count; + Models.Container[] results; + const bool useCeilingAverage = true; + const string fileSearchFilter = "*"; + IDlibDotNet dlibDotNet = GetDlibDotNet(); + const string directorySearchFilter = "*"; + ReadOnlyDictionary>? keyValuePairs = null; + ReadOnlyCollection filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage); + ReadOnlyCollection> filePathsCollection = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection); + (count, results) = GetContainers(dlibDotNet, propertyConfiguration, propertyConfiguration.RootDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, directorySearchFilter); + return (count, results); + } + + internal static List GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) + { + List results = []; + ReadOnlyCollection filteredItems; + foreach (Models.Container container in readOnlyContainers) + { + if (container.Items.Count == 0) + continue; + filteredItems = GetValidImageItems(propertyConfiguration, container); + if (filteredItems.Count == 0) + continue; + foreach (Item item in filteredItems) + { + if (item.Property?.Id is null || item.ResizedFileHolder is null) + continue; + if (results.Contains(item.Property.Id.Value)) + continue; + results.Add(item.Property.Id.Value); + } + } + return results; + } + + internal static List GetFilteredDistinctFileNameFirstSegments(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) + { + List results = []; + ReadOnlyCollection filteredItems; + foreach (Models.Container container in readOnlyContainers) + { + if (container.Items.Count == 0) + continue; + filteredItems = GetValidImageItems(propertyConfiguration, container); + if (filteredItems.Count == 0) + continue; + foreach (Item item in filteredItems) + { + if (item.Property?.Id is null || item.ResizedFileHolder is null) + continue; + if (results.Contains(item.FilePath.FileNameFirstSegment)) + continue; + results.Add(item.FilePath.FileNameFirstSegment); + } + } + return results; + } + + internal static ReadOnlyCollection GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) + { + List results = []; + List distinct = []; + ReadOnlyCollection filteredItems; + foreach (Models.Container container in containers) + { + if (container.Items.Count == 0) + continue; + if (!filterItems) + filteredItems = container.Items; + else + { + filteredItems = GetValidImageItems(propertyConfiguration, container); + if (filteredItems.Count == 0) + continue; + } + foreach (Item item in filteredItems) + { + if (item.Property?.Id is null || item.ResizedFileHolder is null) + continue; + if (distinctItems) + { + if (distinct.Contains(item.Property.Id.Value)) + continue; + distinct.Add(item.Property.Id.Value); + } + results.Add(item); + } + } + return results.AsReadOnly(); + } + +} \ No newline at end of file diff --git a/Container/Models/Stateless/Methods/IContainer.cs b/Container/Models/Stateless/Methods/IContainer.cs new file mode 100644 index 0000000..a877a0f --- /dev/null +++ b/Container/Models/Stateless/Methods/IContainer.cs @@ -0,0 +1,51 @@ +using System.Collections.ObjectModel; +using View_by_Distance.Shared.Models; +using View_by_Distance.Shared.Models.Properties; +using View_by_Distance.Shared.Models.Stateless.Methods; + +namespace View_by_Distance.Container.Models.Stateless.Methods; + +public interface IContainer +{ + + DateTime[] TestStatic_GetContainerDateTimes(ReadOnlyCollection items) => + GetContainerDateTimes(items); + static DateTime[] GetContainerDateTimes(ReadOnlyCollection items) => + Container.GetContainerDateTimes(items); + + ReadOnlyCollection TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) => + GetValidImageItems(propertyConfiguration, container); + static ReadOnlyCollection GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) => + Container.GetValidImageItems(propertyConfiguration, container); + + (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => + GetContainers(propertyConfiguration, aPropertySingletonDirectory); + static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => + GetContainers(propertyConfiguration, null, aPropertySingletonDirectory); + + (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) => + GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory); + static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) => + Container.GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory); + + ReadOnlyCollection TestStatic_GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection) => + GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection); + static ReadOnlyCollection GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection) => + Container.GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection); + + List TestStatic_GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => + GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); + static List GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => + Container.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); + + List TestStatic_GetFilteredDistinctFileNameFirstSegments(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => + GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); + static List GetFilteredDistinctFileNameFirstSegments(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => + Container.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); + + ReadOnlyCollection TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) => + GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems); + static ReadOnlyCollection GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) => + Container.GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems); + +} \ No newline at end of file diff --git a/Date-Group/DateGroup.cs b/Date-Group/DateGroup.cs index 699b44f..3075263 100644 --- a/Date-Group/DateGroup.cs +++ b/Date-Group/DateGroup.cs @@ -57,11 +57,11 @@ public class DateGroup string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}"); if (!Directory.Exists(aPropertySingletonDirectory)) _ = Directory.CreateDirectory(aPropertySingletonDirectory); - (int t, Container[] containers) = Shared.Models.Stateless.Methods.IContainer.GetContainers(propertyConfiguration, aPropertySingletonDirectory); + (int t, Container.Models.Container[] containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(propertyConfiguration, aPropertySingletonDirectory); A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory, aResultsFullGroupDirectory); if (propertyLogic.ExceptionsDirectories.Count != 0) throw new Exception(); - if (propertyConfiguration.PopulatePropertyId && (configuration.ByCreateDateShortcut || configuration.ByHash) && Shared.Models.Stateless.Methods.IProperty.Any(containers)) + if (propertyConfiguration.PopulatePropertyId && (configuration.ByCreateDateShortcut || configuration.ByHash) && Property.Models.Stateless.IProperty.Any(containers)) { propertyLogic.SavePropertyParallelWork(ticks, metadata, t, containers); if (propertyLogic.ExceptionsDirectories.Count != 0) @@ -302,7 +302,7 @@ public class DateGroup return result; } - private static Item[] GetFilterItems(Container container) + private static Item[] GetFilterItems(Container.Models.Container container) { List results = []; foreach (Item item in container.Items) @@ -313,7 +313,7 @@ public class DateGroup return results.ToArray(); } - private (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] GetFileMoveCollectionAll(Property.Models.Configuration configuration, string destinationRoot, Container[] containers) + private (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] GetFileMoveCollectionAll(Property.Models.Configuration configuration, string destinationRoot, Container.Models.Container[] containers) { (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] results; Item[] filteredItems; @@ -322,7 +322,7 @@ public class DateGroup string destinationDirectory; List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollection = []; List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollectionDirectory; - foreach (Container container in containers) + foreach (Container.Models.Container container in containers) { if (container.Items.Count == 0) continue; @@ -352,7 +352,7 @@ public class DateGroup return results; } - private void MoveFiles(Property.Models.Configuration configuration, string destinationRoot, Container[] containers) + private void MoveFiles(Property.Models.Configuration configuration, string destinationRoot, Container.Models.Container[] containers) { string checkDirectory; bool hasDuplicate; @@ -453,7 +453,7 @@ public class DateGroup _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(configuration.RootDirectory); } - private static void CreateDateShortcut(Property.Models.Configuration configuration, Container[] containers) + private static void CreateDateShortcut(Property.Models.Configuration configuration, Container.Models.Container[] containers) { string path; string fileName; @@ -468,17 +468,17 @@ public class DateGroup WindowsShortcut windowsShortcut; TimeSpan threeStandardDeviationHigh; string aPropertyContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "()"); - foreach (Container container in containers) + foreach (Container.Models.Container container in containers) { if (container.Items.Count == 0) continue; selectedTotal = 0; - threeStandardDeviationHigh = Shared.Models.Stateless.Methods.IProperty.GetThreeStandardDeviationHigh(minimum, container); + threeStandardDeviationHigh = Property.Models.Stateless.IProperty.GetThreeStandardDeviationHigh(minimum, container); if (threeStandardDeviationHigh.TotalHours > maximumHours) threeStandardDeviationHigh = new(maximumHours, 0, 0); for (int i = 0; i < container.Items.Count; i++) { - (i, dateTimes, selectedItems) = Shared.Models.Stateless.Methods.IProperty.Get(container, threeStandardDeviationHigh, i); + (i, dateTimes, selectedItems) = Property.Models.Stateless.IProperty.Get(container, threeStandardDeviationHigh, i); selectedTotal += selectedItems.Count; foreach (Item item in selectedItems) { diff --git a/Drag-Drop-Search/Drag-Drop-Search.csproj b/Drag-Drop-Search/Drag-Drop-Search.csproj index 4a00c5c..0ff8c20 100644 --- a/Drag-Drop-Search/Drag-Drop-Search.csproj +++ b/Drag-Drop-Search/Drag-Drop-Search.csproj @@ -34,6 +34,7 @@ + diff --git a/Drag-Drop-Search/DragDropSearch.cs b/Drag-Drop-Search/DragDropSearch.cs index 4afe950..1811159 100644 --- a/Drag-Drop-Search/DragDropSearch.cs +++ b/Drag-Drop-Search/DragDropSearch.cs @@ -107,9 +107,9 @@ public partial class DragDropSearch : Form private void LoadData() { - Container[] containers; + Container.Models.Container[] containers; string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), "{}"); - (_, containers) = IContainer.GetContainers(_Configuration.PropertyConfiguration, aPropertySingletonDirectory); + (_, containers) = View_by_Distance.Container.Models.Stateless.Methods.IContainer.GetContainers(_Configuration.PropertyConfiguration, aPropertySingletonDirectory); List collection = Program.GetItemCollection(_Configuration, containers); foreach (Item item in collection) { diff --git a/Drag-Drop-Search/Program.cs b/Drag-Drop-Search/Program.cs index eaf86ac..cda8153 100644 --- a/Drag-Drop-Search/Program.cs +++ b/Drag-Drop-Search/Program.cs @@ -12,7 +12,7 @@ public class Program // ApplicationConfiguration.Initialize(); Application.Run(new DragDropSearch()); - private static Item[] GetFilterItems(Models.Configuration configuration, Container container) + private static Item[] GetFilterItems(Models.Configuration configuration, Container.Models.Container container) { List results = []; foreach (Item item in container.Items) @@ -24,11 +24,11 @@ public class Program return results.ToArray(); } - public static List GetItemCollection(Models.Configuration configuration, Container[] containers) + public static List GetItemCollection(Models.Configuration configuration, Container.Models.Container[] containers) { List results = []; Item[] filteredItems; - foreach (Container container in containers) + foreach (Container.Models.Container container in containers) { if (container.Items.Count == 0) continue; diff --git a/Duplicate-Search/DuplicateSearch.cs b/Duplicate-Search/DuplicateSearch.cs index 2ac2c2f..82e1694 100644 --- a/Duplicate-Search/DuplicateSearch.cs +++ b/Duplicate-Search/DuplicateSearch.cs @@ -30,7 +30,7 @@ public class DuplicateSearch else { Configuration.Verify(configuration, requireExist: false); - Container[] containers = GetContainers(ticks, configuration); + Container.Models.Container[] containers = GetContainers(ticks, configuration); string argZero = args.Count > 0 ? Path.GetFullPath(args[0]) : Path.GetFullPath(configuration.RootDirectory); bool argZeroIsConfigurationRootDirectory = configuration.RootDirectory == argZero; string destinationRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(configuration, "Z) Moved"); @@ -125,10 +125,10 @@ public class DuplicateSearch } } - private static Container[] GetContainers(long ticks, Configuration configuration) + private static Container.Models.Container[] GetContainers(long ticks, Configuration configuration) { int f; - Container[] containers; + Container.Models.Container[] containers; int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string message = $") Building Container(s) - {totalSeconds} total second(s)"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; @@ -136,12 +136,12 @@ public class DuplicateSearch { progressBar.Tick(); string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "{}"); - (f, containers) = Shared.Models.Stateless.Methods.IContainer.GetContainers(configuration, aPropertySingletonDirectory); + (f, containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(configuration, aPropertySingletonDirectory); } return containers; } - private static Dictionary> GetIdToCollection(string argZero, Configuration configuration, bool argZeroIsConfigurationRootDirectory, Container[] containers, string destinationRoot, List preloadIds) + private static Dictionary> GetIdToCollection(string argZero, Configuration configuration, bool argZeroIsConfigurationRootDirectory, Container.Models.Container[] containers, string destinationRoot, List preloadIds) { Dictionary> results = []; string directory; @@ -157,16 +157,16 @@ public class DuplicateSearch foreach (int id in preloadIds) results.Add(id, [null]); } - foreach (Container container in containers) + foreach (Container.Models.Container container in containers) { if (container.Items.Count == 0) continue; if (!argZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) continue; - validImageItems = Shared.Models.Stateless.Methods.IContainer.GetValidImageItems(configuration, container); + validImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(configuration, container); if (validImageItems.Count == 0) continue; - containerDateTimes = Shared.Models.Stateless.Methods.IContainer.GetContainerDateTimes(validImageItems); + containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(validImageItems); foreach (Item item in validImageItems) { if (item.Property?.Id is null) diff --git a/FaceParts/Models/_D2_FaceParts.cs b/FaceParts/Models/_D2_FaceParts.cs index 3cff1a3..2164d5b 100644 --- a/FaceParts/Models/_D2_FaceParts.cs +++ b/FaceParts/Models/_D2_FaceParts.cs @@ -403,7 +403,7 @@ public class D2_FaceParts } } - public void SaveFaceLandmarkImages(Configuration configuration, string d2ResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List faces, bool saveRotated) + public void SaveFaceLandmarkImages(Property.Models.Configuration configuration, string d2ResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List faces, bool saveRotated) { FileInfo fileInfo; bool check = false; diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index dd5281f..9729cc4 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -300,6 +300,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string message; MapLogic? mapLogic; A_Property propertyLogic; + IDlibDotNet dlibDotNet = this; string eDistanceContentDirectory; string? a2PeopleContentDirectory; string aResultsFullGroupDirectory; @@ -314,15 +315,17 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable bool configurationOutputResolutionsHas = false; ReadOnlyDictionary> personKeyToIds; ReadOnlyDictionary? splatNineIdentifiers = null; + ReadOnlyDictionary>? keyValuePairs = null; ReadOnlyCollection>? filePathsCollection = null; bool runToDoCollectionFirst = GetRunToDoCollectionFirst(_Configuration, ticks); - (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = GetResultsFullGroupDirectories(); + (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(_Configuration.PropertyConfiguration.RootDirectory, ticks); a2PeopleContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), "([])"); + B_Metadata metadata = new(_Configuration.PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, bResultsFullGroupDirectory); eDistanceContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(E_Distance), _Configuration.PropertyConfiguration.ResultContent); string a2PeopleSingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), _Configuration.PropertyConfiguration.ResultSingleton); - _ = Directory.CreateDirectory(Path.Combine(eDistanceContentDirectory, $"{ticks}")); + _ = Directory.CreateDirectory(Path.Combine(eDistanceContentDirectory, ticks.ToString())); if (runToDoCollectionFirst) mapLogic = null; else @@ -335,7 +338,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable if (!runToDoCollectionFirst) break; (filesCollectionRootDirectory, filePathsCollection, filesCollectionCountIsOne) = GetFilesCollectionThenCopyOrMove(ticks, fileSearchFilter, directorySearchFilter, options, outputResolution); - splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, filePathsCollection); + keyValuePairs = FilePath.GetKeyValuePairs(filePathsCollection); + splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, keyValuePairs); break; } fPhotoPrismContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultContent); @@ -357,7 +361,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } argZero = SaveUrlAndGetNewRootDirectory(filePathsCollection.First()); _Configuration.PropertyConfiguration.ChangeRootDirectory(argZero); - (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = GetResultsFullGroupDirectories(); + (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); propertyRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), create: false); propertyLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FileNameExtension, _Configuration.Reverse, aResultsFullGroupDirectory); } @@ -367,7 +371,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable { if (outputResolution.Any(char.IsNumber)) continue; - (cResultsFullGroupDirectory, _, _, _) = GetResultsFullGroupDirectories(outputResolution); + (cResultsFullGroupDirectory, _, _, _) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); filesCollectionRootDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useCeilingAverage: true); break; @@ -375,20 +379,17 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } if (filesCollectionRootDirectory is null || filePathsCollection is null) throw new NullReferenceException(nameof(filePathsCollection)); - string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton); - if (!Directory.Exists(aPropertySingletonDirectory)) - _ = Directory.CreateDirectory(aPropertySingletonDirectory); int count = filePathsCollection.Select(l => l.Count).Sum(); message = $") Building Container(s) - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; _ProgressBar = new(count, message, options); - ReadOnlyCollection readOnlyContainers = Shared.Models.Stateless.Methods.IContainer.GetContainers(this, _Configuration.PropertyConfiguration, aPropertySingletonDirectory, filesCollectionRootDirectory, splatNineIdentifiers, filePathsCollection); + ReadOnlyCollection readOnlyContainers = Container.Models.Stateless.Methods.IContainer.GetContainers(this, _Configuration.PropertyConfiguration, _Faces.FileNameExtension, _Faces.HiddenFileNameExtension, eDistanceContentDirectory, filesCollectionRootDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection); _ProgressBar.Dispose(); mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); DeleteContinueFiles(personContainers); if (!runToDoCollectionFirst) MapFaceFileLogic(ticks, personContainers, mapLogic, a2PeopleContentDirectory, eDistanceContentDirectory, options); - FullDoWork(argZero, propertyRoot, ticks, aResultsFullGroupDirectory, bResultsFullGroupDirectory, fPhotoPrismSingletonDirectory, count, readOnlyContainers, propertyLogic, mapLogic); - ReadOnlyCollection distinctValidImageItems = Shared.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true); + FullDoWork(argZero, propertyRoot, ticks, aResultsFullGroupDirectory, fPhotoPrismSingletonDirectory, count, metadata, readOnlyContainers, propertyLogic, mapLogic); + ReadOnlyCollection distinctValidImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true); if (_Configuration.LookForAbandoned) { string dResultsFullGroupDirectory; @@ -396,7 +397,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable foreach (string outputResolution in _Configuration.OutputResolutions) { _ProgressBar = new(5, nameof(mapLogic.LookForAbandoned), options); - (cResultsFullGroupDirectory, _, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = GetResultsFullGroupDirectories(outputResolution); + (cResultsFullGroupDirectory, _, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); mapLogic.LookForAbandoned(this, _Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); _ProgressBar.Dispose(); } @@ -437,7 +438,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string dResultsFullGroupDirectory; string c2ResultsFullGroupDirectory; string d2ResultsFullGroupDirectory; - (cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = GetResultsFullGroupDirectories(outputResolution); + (cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(aResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton)); _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(bResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton)); _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton)); @@ -549,33 +550,32 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return result; } - private void FullDoWork(string argZero, string propertyRoot, long ticks, string aResultsFullGroupDirectory, string bResultsFullGroupDirectory, string fPhotoPrismSingletonDirectory, int count, ReadOnlyCollection readOnlyContainers, A_Property propertyLogic, MapLogic mapLogic) + private void FullDoWork(string argZero, string propertyRoot, long ticks, string aResultsFullGroupDirectory, string fPhotoPrismSingletonDirectory, int count, B_Metadata metadata, ReadOnlyCollection readOnlyContainers, A_Property propertyLogic, MapLogic mapLogic) { int total; int notMapped; string message; bool exceptions; int totalSeconds; - Container container; int totalNotMapped = 0; + IDlibDotNet dlibDotNet = this; bool outputResolutionHasNumber; bool anyNullOrNoIsUniqueFileName; string cResultsFullGroupDirectory; string dResultsFullGroupDirectory; string c2ResultsFullGroupDirectory; string d2ResultsFullGroupDirectory; + Container.Models.Container container; ReadOnlyCollection filteredItems; - int containersLength = readOnlyContainers.Count; List> sourceDirectoryChanges = []; int maxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism; Dictionary> fileNameToCollection = !Directory.Exists(fPhotoPrismSingletonDirectory) ? fileNameToCollection = [] : F_PhotoPrism.GetFileNameToCollection(fPhotoPrismSingletonDirectory); string dResultsDateGroupDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(D_Face)); - B_Metadata metadata = new(_Configuration.PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, bResultsFullGroupDirectory); foreach (string outputResolution in _Configuration.OutputResolutions) { total = 0; outputResolutionHasNumber = outputResolution.Any(char.IsNumber); - (cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = GetResultsFullGroupDirectories(outputResolution); + (cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); _Faces.Update(dResultsFullGroupDirectory); _Resize.Update(cResultsFullGroupDirectory); _FaceParts.Update(d2ResultsFullGroupDirectory); @@ -587,13 +587,13 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable continue; if (!_ArgZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) continue; - filteredItems = Shared.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, container); + filteredItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, container); if (filteredItems.Count == 0) continue; sourceDirectoryChanges.Clear(); anyNullOrNoIsUniqueFileName = filteredItems.Any(l => !l.IsUniqueFileName); totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); - message = $"{i + 1:000} [{filteredItems.Count:000}] / {containersLength:000} - {total} / {count} total - {totalSeconds} total second(s) - {outputResolution} - <{container.SourceDirectory}> - total not mapped {totalNotMapped:000000}"; + message = $"{totalSeconds} total second(s) - {outputResolution} - {i + 1:000} / {readOnlyContainers.Count:000} - {total} / {count} total - <{container.SourceDirectory}> [{filteredItems.Count:000}] - total not mapped {totalNotMapped:000000}"; propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, container.SourceDirectory, anyNullOrNoIsUniqueFileName); if (outputResolutionHasNumber) _Resize.SetAngleBracketCollection(cResultsFullGroupDirectory, container.SourceDirectory); @@ -629,7 +629,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable total += container.Items.Count; } totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); - message = $"### [###] / {containersLength:000} - {total} / {count} total - {totalSeconds} total second(s) - {outputResolution} - <> - total not mapped {totalNotMapped:000000}"; + message = $"{totalSeconds} total second(s) - {outputResolution} - ### [###] / {readOnlyContainers.Count:000} - {total} / {count} total - <> - total not mapped {totalNotMapped:000000}"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using ProgressBar progressBar = new(1, message, options); progressBar.Tick(); @@ -678,14 +678,24 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return results.AsReadOnly(); } + private static Identifier GetIdentifier(Property.Models.Configuration propertyConfiguration, FilePath filePath, KeyValuePair> keyValuePair) + { + Identifier result; + if (filePath.Id is null) + throw new Exception(); + string[] directoryNames = keyValuePair.Value.Select(l => l.DirectoryFullPath.Replace('\\', '/')).ToArray(); + string paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, index: null); + result = new(directoryNames, filePath.ExtensionLowered, filePath.HasDateTimeOriginal, filePath.Id.Value, filePath.Length, paddedId, filePath.LastWriteTicks); + return result; + } + private static ReadOnlyDictionary GetSplatNineIdentifiers(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyDictionary> keyValuePairs) { Dictionary results = []; string json; - string paddedId; FilePath? filePath; Identifier identifier; - string[] directoryNames; + List identifiers = []; string rootDirectory = propertyConfiguration.RootDirectory.Replace('\\', '/'); string bMetadataCollectionDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultCollection); if (!Directory.Exists(bMetadataCollectionDirectory)) @@ -700,24 +710,31 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable break; filePath = null; } - if (filePath?.Id is null) + if (filePath is null) + { + filePath = keyValuePair.Value[0]; + if (filePath.Id is null) + continue; + identifier = GetIdentifier(propertyConfiguration, filePath, keyValuePair); + identifiers.Add(identifier); continue; - directoryNames = keyValuePair.Value.Select(l => l.DirectoryFullPath.Replace('\\', '/')).ToArray(); - paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, index: null); - identifier = new(directoryNames, filePath.ExtensionLowered, filePath.HasDateTimeOriginal, filePath.Id.Value, filePath.Length, paddedId, filePath.LastWriteTicks); + } + else if (filePath.Id is null) + continue; + identifier = GetIdentifier(propertyConfiguration, filePath, keyValuePair); + identifiers.Add(identifier); results.Add(keyValuePair.Key, identifier); } json = JsonSerializer.Serialize(results.Values.ToArray(), IdentifierCollectionSourceGenerationContext.Default.IdentifierArray); _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(bMetadataCollectionDirectory, "!9.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); - json = JsonSerializer.Serialize((from l in results orderby l.Value.PaddedId select l.Value).ToArray(), IdentifierCollectionSourceGenerationContext.Default.IdentifierArray); + json = JsonSerializer.Serialize((from l in identifiers orderby l.PaddedId select l).ToArray(), IdentifierCollectionSourceGenerationContext.Default.IdentifierArray); _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(bMetadataCollectionDirectory, ".json"), json.Replace(rootDirectory, string.Empty), updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); return results.AsReadOnly(); } - private static ReadOnlyDictionary GetSplatNineIdentifiersAndHideSplatNine(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection> filePathsCollection) + private static ReadOnlyDictionary GetSplatNineIdentifiersAndHideSplatNine(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyDictionary> keyValuePairs) { ReadOnlyDictionary results; - ReadOnlyDictionary> keyValuePairs = FilePath.GetKeyValuePairs(filePathsCollection); if (keyValuePairs.Count == 0) results = new(new Dictionary()); else @@ -730,7 +747,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return results; } - private ReadOnlyCollection GetMappings(Property.Models.Configuration propertyConfiguration, string eDistanceContentDirectory, ReadOnlyCollection readOnlyContainers, MapLogic mapLogic, bool distinctItems) + private ReadOnlyCollection GetMappings(Property.Models.Configuration propertyConfiguration, string eDistanceContentDirectory, ReadOnlyCollection readOnlyContainers, MapLogic mapLogic, bool distinctItems) { List results = []; int count = 0; @@ -743,14 +760,14 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable DateTime[] containerDateTimes; MappingFromItem mappingFromItem; ReadOnlyCollection filteredItems; - foreach (Container container in readOnlyContainers) + foreach (Container.Models.Container container in readOnlyContainers) { if (container.Items.Count == 0) continue; - filteredItems = Shared.Models.Stateless.Methods.IContainer.GetValidImageItems(propertyConfiguration, container); + filteredItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(propertyConfiguration, container); if (filteredItems.Count == 0) continue; - containerDateTimes = Shared.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems); + containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems); focusRelativePath = Path.GetFullPath(string.Concat(_Configuration.PropertyConfiguration.RootDirectory, _Configuration.FocusDirectory)); isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath); foreach (Item item in filteredItems) @@ -927,8 +944,9 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable private List GetSaveContainers(long ticks, ReadOnlyCollection personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, ProgressBarOptions options, MapLogic mapLogic, string outputResolution) { List results; + IDlibDotNet dlibDotNet = this; long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray(); - (string cResultsFullGroupDirectory, string _, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) = GetResultsFullGroupDirectories(outputResolution); + (string cResultsFullGroupDirectory, string _, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); ReadOnlyDictionary> mapped = Map.Models.Stateless.Methods.IMapLogic.GetMapped(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory); if (mapped.Count == 0 && !_Configuration.SaveSortingWithoutPerson) @@ -968,9 +986,10 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return results; } - private void MapLogic(long ticks, ReadOnlyCollection containers, string fPhotoPrismContentDirectory, MapLogic mapLogic, string outputResolution, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageFaces, ReadOnlyCollection distinctValidImageMappingCollection) + private void MapLogic(long ticks, ReadOnlyCollection containers, string fPhotoPrismContentDirectory, MapLogic mapLogic, string outputResolution, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageFaces, ReadOnlyCollection distinctValidImageMappingCollection) { - (_, _, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) = GetResultsFullGroupDirectories(outputResolution); + IDlibDotNet dlibDotNet = this; + (_, _, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); string d2FacePartsContentDirectory = Path.Combine(d2ResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); string d2FacePartsContentCollectionDirectory = Path.Combine(d2ResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContentCollection); @@ -1141,7 +1160,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string d2ResultsFullGroupDirectory, List> sourceDirectoryChanges, Dictionary> fileNameToCollection, - Container container, + Container.Models.Container container, Item item, DateTime[] containerDateTimes, bool? isFocusRelativePath) @@ -1202,12 +1221,10 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } if (resizedFileHolder.Exists && item.FilePath.HasIgnoreKeyword is not null && item.FilePath.HasIgnoreKeyword.Value != shouldIgnore.Value) { - if (!item.FilePath.DirectoryFullPath.Contains("Results") || !item.FilePath.DirectoryFullPath.Contains("Resize")) - throw new NotSupportedException($"Rename File! <{item.FilePath.FileNameFirstSegment}>"); - else - { + if (item.FilePath.DirectoryFullPath.Contains("Results") && item.FilePath.DirectoryFullPath.Contains("Resize")) File.Delete(resizedFileHolder.FullName); - } + else + throw new NotSupportedException($"Rename File! <{item.FilePath.FileNameFirstSegment}>"); } } if (property is null || item.Property is null) @@ -1297,14 +1314,14 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string d2ResultsFullGroupDirectory, List> sourceDirectoryChanges, Dictionary> fileNameToCollection, - Container container, + Container.Models.Container container, ReadOnlyCollection filteredItems, string message) { int result = 0; int exceptionsCount = 0; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; - DateTime[] containerDateTimes = Shared.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems); + DateTime[] containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems); ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; string focusRelativePath = Path.GetFullPath(string.Concat(_Configuration.PropertyConfiguration.RootDirectory, _Configuration.FocusDirectory)); bool? isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath); @@ -1340,7 +1357,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return (result, exceptionsCount > 0); } - private (string, string) GetResultsFullGroupDirectories() + (string, string) IDlibDotNet.GetResultsFullGroupDirectories() { string aResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory( _Configuration.PropertyConfiguration, @@ -1359,7 +1376,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return new(aResultsFullGroupDirectory, bResultsFullGroupDirectory); } - private (string, string, string, string) GetResultsFullGroupDirectories(string outputResolution) + (string, string, string, string) IDlibDotNet.GetResultsFullGroupDirectories(string outputResolution) { string cResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory( _Configuration.PropertyConfiguration, @@ -1503,8 +1520,9 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable private (string, ReadOnlyCollection>, bool) GetFilesCollectionThenCopyOrMove(long ticks, string fileSearchFilter, string directorySearchFilter, ProgressBarOptions options, string outputResolution) { ProgressBar progressBar; + IDlibDotNet dlibDotNet = this; string filesCollectionRootDirectory = _Configuration.PropertyConfiguration.RootDirectory; - (string cResultsFullGroupDirectory, _, _, _) = GetResultsFullGroupDirectories(outputResolution); + (string cResultsFullGroupDirectory, _, _, _) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); IReadOnlyDictionary>> fileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, [_Configuration.PropertyConfiguration.ResultContent, _Configuration.PropertyConfiguration.ResultContentCollection]); ReadOnlyCollection> filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useCeilingAverage: false); int count = filePathsCollection.Select(l => l.Count).Sum(); diff --git a/Instance/Instance.csproj b/Instance/Instance.csproj index d8b80ac..1aaef30 100644 --- a/Instance/Instance.csproj +++ b/Instance/Instance.csproj @@ -51,6 +51,7 @@ + diff --git a/Map/Map.csproj b/Map/Map.csproj index 5d56d1e..643acd2 100644 --- a/Map/Map.csproj +++ b/Map/Map.csproj @@ -41,6 +41,8 @@ + + \ No newline at end of file diff --git a/Map/Models/MapLogic.cs b/Map/Models/MapLogic.cs index c2e5fd4..7bbc980 100644 --- a/Map/Models/MapLogic.cs +++ b/Map/Models/MapLogic.cs @@ -460,7 +460,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic return result; } - private (long?, string?) GetDirectory(Configuration configuration, string by, string segmentB) + private (long?, string?) GetDirectory(Configuration configuration, string by, LocationContainer _, string segmentB) { long? ticks = null; const int zero = 0; @@ -537,22 +537,22 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic return result; } - private Record Get(Configuration configuration, string by, long? personKey, string? displayDirectoryName, string segmentB) + private Record Get(Configuration configuration, string by, LocationContainer locationContainer, string? displayDirectoryName, string segmentB) { long? ticks; string? directory; string? debugDirectory; string? personDirectory; - if (personKey is null || string.IsNullOrEmpty(displayDirectoryName)) + if (locationContainer.PersonKey is null || string.IsNullOrEmpty(displayDirectoryName)) { debugDirectory = null; - (ticks, directory) = GetDirectory(configuration, by, segmentB); + (ticks, directory) = GetDirectory(configuration, by, locationContainer, segmentB); personDirectory = directory is null ? null : Path.Combine(directory, $"X+{ticks}"); } else { ticks = null; - string personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personKey.Value); + string personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, locationContainer.PersonKey.Value); debugDirectory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, displayDirectoryName); directory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, segmentB); personDirectory = Path.Combine(directory, displayDirectoryName, "lnk"); @@ -880,7 +880,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic displayDirectoryName = GetDisplayDirectoryName(locationContainer.DisplayDirectoryName, locationContainer, personKeyToPersonContainer); isCounterPersonYear = locationContainer.PersonKey is not null && IPersonBirthday.IsCounterPersonYear(locationContainer.PersonKey.Value); (by, _, isBySorting) = Stateless.MapLogic.Get(useFiltersCounter, _Configuration.SaveIndividually, sortingContainersAny, forceSingleImageHumanized, locationContainer.LengthPermyriad, locationContainer.PersonKey, displayDirectoryName); - record = Get(_Configuration, by, locationContainer.PersonKey, displayDirectoryName, segmentB); + record = Get(_Configuration, by, locationContainer, displayDirectoryName, segmentB); if (string.IsNullOrEmpty(record.Directory) || string.IsNullOrEmpty(record.PersonDirectory)) continue; directory = record.Directory; @@ -1406,14 +1406,14 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic return new(directoriesAndDateTimes, collection); } - public void SaveShortcutsForOutputResolutionsDuringMapLogic(ReadOnlyCollection containers, ReadOnlyDictionary> personKeyToIds, string dFacesContentDirectory, ReadOnlyCollection distinctValidImageMappingCollection) + public void SaveShortcutsForOutputResolutionsDuringMapLogic(ReadOnlyCollection containers, ReadOnlyDictionary> personKeyToIds, string dFacesContentDirectory, ReadOnlyCollection distinctValidImageMappingCollection) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); WindowsShortcut windowsShortcut; List<(string, DateTime[])> directoriesAndDateTimes; List collection; - ReadOnlyCollection validImageItems = IContainer.GetValidImageItems(_PropertyConfiguration, containers, distinctItems: true, filterItems: true); + ReadOnlyCollection validImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_PropertyConfiguration, containers, distinctItems: true, filterItems: true); (directoriesAndDateTimes, collection) = GetCollectionForSaveShortcutsForOutputResolutionsDuringMapLogic(personKeyToIds, dFacesContentDirectory, validImageItems, distinctValidImageMappingCollection); string[] directories = (from l in collection select l.Directory).Distinct().ToArray(); foreach (string directory in directories) @@ -1499,14 +1499,14 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic public bool? IsFocusPerson(int? skipPersonWithMoreThen, long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) => IsFocusPerson(skipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation.WholePercentages); - public void LookForAbandoned(IDlibDotNet dlibDotNet, Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) + public void LookForAbandoned(IDlibDotNet dlibDotNet, Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) { string[] directories; string? directoryName; - List distinctFilteredIds = IContainer.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); + List distinctFilteredIds = Container.Models.Stateless.Methods.IContainer.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); LookForAbandoned(propertyConfiguration, distinctFilteredIds); dlibDotNet.Tick(); - List distinctFilteredFileNameFirstSegments = IContainer.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); + List distinctFilteredFileNameFirstSegments = Container.Models.Stateless.Methods.IContainer.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, bResultsFullGroupDirectory, distinctFilteredFileNameFirstSegments); dlibDotNet.Tick(); directories = Directory.GetDirectories(cResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); diff --git a/Map/Models/Stateless/DistanceLogic.cs b/Map/Models/Stateless/DistanceLogic.cs index 5b1e341..93aa685 100644 --- a/Map/Models/Stateless/DistanceLogic.cs +++ b/Map/Models/Stateless/DistanceLogic.cs @@ -22,7 +22,6 @@ internal abstract class DistanceLogic internal record TicksDirectory(DateTime AlternateDirectoryDateTime, string Directory, DateTime DirectoryDateTime, - string DirectoryName, bool? IsLocationContainerDebugDirectory, float? TotalDays); @@ -86,53 +85,55 @@ internal abstract class DistanceLogic float? totalDays; long? next = null; string? checkDirectory; - string ticksDirectoryName; DateTime directoryDateTime; DirectoryInfo directoryInfo; + TicksDirectory ticksDirectory; long? lastDirectoryTicks = null; DateTime dateTime = DateTime.Now; DateTime alternateDirectoryDateTime; + string ticksDirectoryNameFirstSegment; bool? isLocationContainerDebugDirectory; long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks; for (int i = 1; i < 5; i++) _ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory); if (!Directory.Exists(eDistanceContentDirectory)) _ = Directory.CreateDirectory(eDistanceContentDirectory); - string[] ticksDirectories = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string ticksDirectory in ticksDirectories) + string[] ticksFullPaths = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string ticksFullPath in ticksFullPaths) { - ticksDirectoryName = Path.GetFileName(ticksDirectory); - if (ticksDirectoryName.Length < 3) + ticksDirectoryNameFirstSegment = Path.GetFileName(ticksFullPath).Split('.')[0]; + if (ticksDirectoryNameFirstSegment.Length < 3) continue; - if (!long.TryParse(ticksDirectoryName, out long directoryTicks)) + if (!long.TryParse(ticksDirectoryNameFirstSegment, out long directoryTicks)) throw new NotSupportedException(); if (next is null) next = new DateTime(directoryTicks).Ticks; else { next += month; - checkDirectory = Path.GetDirectoryName(ticksDirectory); + checkDirectory = Path.GetDirectoryName(ticksFullPath); if (string.IsNullOrEmpty(checkDirectory)) { if (string.IsNullOrEmpty(checkDirectory)) continue; checkDirectory = Path.Combine(checkDirectory, next.Value.ToString()); - if (ticksDirectory == checkDirectory || !checkDirectory.EndsWith(configuration.LocationContainerDirectoryPattern)) + if (ticksFullPath == checkDirectory || !checkDirectory.EndsWith(configuration.LocationContainerDirectoryPattern)) continue; - Directory.Move(ticksDirectory, checkDirectory); + Directory.Move(ticksFullPath, checkDirectory); continue; } } - directoryInfo = new(ticksDirectory); + directoryInfo = new(ticksFullPath); directoryDateTime = new DateTime(directoryTicks); if (directoryInfo.CreationTime.Ticks != directoryTicks) - Directory.SetCreationTime(ticksDirectory, new DateTime(directoryTicks)); + Directory.SetCreationTime(ticksFullPath, new DateTime(directoryTicks)); if (directoryInfo.LastWriteTime.Ticks != directoryTicks) - Directory.SetLastWriteTime(ticksDirectory, new DateTime(directoryTicks)); + Directory.SetLastWriteTime(ticksFullPath, new DateTime(directoryTicks)); alternateDirectoryDateTime = new DateTime(directoryDateTime.Year, directoryDateTime.Month, directoryDateTime.Day).AddMonths(1); - isLocationContainerDebugDirectory = configuration.LocationContainerDebugDirectory is null ? null : ticksDirectoryName.EndsWith(configuration.LocationContainerDebugDirectory); + isLocationContainerDebugDirectory = configuration.LocationContainerDebugDirectory is null ? null : ticksDirectoryNameFirstSegment.EndsWith(configuration.LocationContainerDebugDirectory); totalDays = lastDirectoryTicks is null || new TimeSpan(dateTime.Ticks - directoryTicks).TotalDays < 1 ? null : (float)new TimeSpan(directoryTicks - lastDirectoryTicks.Value).TotalDays; - results.Add(new(alternateDirectoryDateTime, ticksDirectory, new(directoryTicks), ticksDirectoryName, isLocationContainerDebugDirectory, totalDays)); + ticksDirectory = new(alternateDirectoryDateTime, ticksFullPath, new(directoryTicks), isLocationContainerDebugDirectory, totalDays); + results.Add(ticksDirectory); if (directoryDateTime.Hour == 0 && directoryDateTime.Minute == 0 && directoryDateTime.Second == 0) continue; lastDirectoryTicks = directoryTicks; @@ -216,9 +217,37 @@ internal abstract class DistanceLogic } } - private static List GetRecords(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, bool? isDefault, string[] files, int directoryNumber, string personKeyFormatted, int? linksCount, List distinct, string? personDisplayDirectoryName) + private static string GetCheckFile(TicksDirectory ticksDirectory, string @enum, string fileName, string file) + { + string result; + string checkDirectory; + string directory = file; + List collection = []; + for (int i = 0; i < file.Length; i++) + { + directory = Path.GetDirectoryName(directory) ?? throw new Exception(); + if (directory == ticksDirectory.Directory) + break; + collection.Add(Path.GetFileName(directory)); + } + collection.Reverse(); + checkDirectory = $"{ticksDirectory.Directory}.{@enum}"; + foreach (string directoryName in collection) + checkDirectory = Path.Combine(checkDirectory, directoryName); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + result = Path.Combine(checkDirectory, fileName); + if (File.Exists(result)) + throw new Exception($"File <{fileName}> already exists!"); + File.Move(file, result); + return result; + } + + private static List GetRecords(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, TicksDirectory ticksDirectory, bool? isDefault, string[] files, int directoryNumber, string personKeyFormatted, int? linksCount, List distinct, string? personDisplayDirectoryName) { List results = []; + string @enum; + Record record; string fileName; string checkFile; FilePath filePath; @@ -244,8 +273,26 @@ internal abstract class DistanceLogic File.Move(file, checkFile); continue; } + if (file.StartsWith(ticksDirectory.Directory)) + { + @enum = IPath.GetEnum(filePath).ToString(); + if (!ticksDirectory.Directory.EndsWith(@enum)) + { + checkFile = GetCheckFile(ticksDirectory, @enum, fileName, file); + fileHolder = IFileHolder.Get(checkFile); + filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); + if (filePath.Id is null) + continue; + } + } distinct.Add(fileName); - results.Add(new(directoryNumber, isDefault, linksCount, filePath, personDisplayDirectoryName, personKeyFormatted)); + record = new(DirectoryNumber: directoryNumber, + IsDefault: isDefault, + LinksCount: linksCount, + MappedFaceFilePath: filePath, + PersonDisplayDirectoryName: personDisplayDirectoryName, + PersonKeyFormatted: personKeyFormatted); + results.Add(record); } return results; } @@ -311,6 +358,7 @@ internal abstract class DistanceLogic DateTime dateTime; TimeSpan timeSpan; int directoryNumber; + List records; string? checkDirectory; ProgressBar progressBar; string[] yearDirectories; @@ -340,8 +388,7 @@ internal abstract class DistanceLogic progressBar = new(ticksDirectories.Count, message, options); foreach (TicksDirectory ticksDirectory in ticksDirectories) { - if (i == 1) - progressBar.Tick(); + progressBar.Tick(); personKeyFormattedDirectories = Directory.GetDirectories(ticksDirectory.Directory, "*", SearchOption.TopDirectoryOnly); foreach (string personKeyFormattedDirectory in personKeyFormattedDirectories) { @@ -368,23 +415,6 @@ internal abstract class DistanceLogic linksCount = null; else linksCount = GetLinksCount(yearDirectory); - if (ticksDirectory.DirectoryName != configuration.LocationContainerDebugDirectory) - { - files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string file in files) - File.Delete(file); - } - if (ticksDirectory.DirectoryName == configuration.LocationContainerDebugDirectory) - { - isDefault = null; - personDisplayDirectoryName = null; - files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly); - results.AddRange(GetRecords(propertyConfiguration, configuration, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName)); - files = Directory.GetFiles(yearDirectory, "*.lnk", SearchOption.AllDirectories); - foreach (string file in files) - File.Delete(file); - continue; - } personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly); if (personNameDirectories.Length > 1) throw new NotSupportedException("Try deleting *.lnk files!"); @@ -467,7 +497,9 @@ internal abstract class DistanceLogic Directory.Move(personNameDirectory, personFirstInitialDirectory); files = Directory.GetFiles(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly); } - results.AddRange(GetRecords(propertyConfiguration, configuration, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName)); + records = GetRecords(propertyConfiguration, configuration, ticksDirectory, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName); + if (records.Count > 0) + results.AddRange(records); personNameLinkDirectories = Directory.GetDirectories(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string personNameLinkDirectory in personNameLinkDirectories) { diff --git a/Map/Models/Stateless/MapLogic.cs b/Map/Models/Stateless/MapLogic.cs index 3c106ff..72cb0cc 100644 --- a/Map/Models/Stateless/MapLogic.cs +++ b/Map/Models/Stateless/MapLogic.cs @@ -13,17 +13,17 @@ namespace View_by_Distance.Map.Models.Stateless; internal abstract class MapLogic { + internal record Duplicate(long PersonKey, + int Id, + FilePath FilePath, + float? Percent); + internal record MappedFile(long PersonKey, string PersonKeyFormatted, string? PersonDisplayDirectoryName, int? DirectoryNumber, FilePath FilePath); - internal record Duplicate(long PersonKey, - int Id, - FilePath FilePath, - float? Percent); - internal record PersonKeyFormattedIdThenWholePercentages(string PersonKeyFormatted, string? PersonDisplayDirectoryName, bool? IsDefault, @@ -188,6 +188,7 @@ internal abstract class MapLogic private static List GetDisplayDirectoryAllFiles(string fileNameExtension, string personBirthdayFormat, ReadOnlyCollection personContainers) { List results = []; + MappedFile mappedFile; string personKeyFormatted; List distinct = []; foreach (PersonContainer personContainer in personContainers) @@ -202,7 +203,12 @@ internal abstract class MapLogic continue; distinct.Add(personContainer.DisplayDirectoryAllFilePaths[i].Name); personKeyFormatted = IPersonBirthday.GetFormatted(personBirthdayFormat, personContainer.Key.Value); - results.Add(new(personContainer.Key.Value, personKeyFormatted, personContainer.DisplayDirectoryName, null, personContainer.DisplayDirectoryAllFilePaths[i])); + mappedFile = new(PersonKey: personContainer.Key.Value, + PersonKeyFormatted: personKeyFormatted, + PersonDisplayDirectoryName: personContainer.DisplayDirectoryName, + DirectoryNumber: null, + FilePath: personContainer.DisplayDirectoryAllFilePaths[i]); + results.Add(mappedFile); } } return results; @@ -462,6 +468,7 @@ internal abstract class MapLogic string checkFile; FilePath filePath; FileHolder fileHolder; + MappedFile mappedFile; List distinct = []; PersonBirthday? personBirthday; PersonContainer? personContainer; @@ -481,7 +488,12 @@ internal abstract class MapLogic personDisplayDirectoryName = record.PersonDisplayDirectoryName; else personDisplayDirectoryName = personContainer.DisplayDirectoryName; - results.Add(new(personKey, record.PersonKeyFormatted, personDisplayDirectoryName, record.DirectoryNumber, record.MappedFaceFilePath)); + mappedFile = new(PersonKey: personKey, + PersonKeyFormatted: record.PersonKeyFormatted, + PersonDisplayDirectoryName: personDisplayDirectoryName, + DirectoryNumber: record.DirectoryNumber, + FilePath: record.MappedFaceFilePath); + results.Add(mappedFile); } for (int i = results.Count - 1; i > -1; i--) { @@ -501,7 +513,11 @@ internal abstract class MapLogic File.Move(filePath.FullName, checkFile); fileHolder = IFileHolder.Get(checkFile); filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); - results[i] = new(results[i].PersonKey, results[i].PersonKeyFormatted, results[i].PersonDisplayDirectoryName, results[i].DirectoryNumber, filePath); + results[i] = new(PersonKey: results[i].PersonKey, + PersonKeyFormatted: results[i].PersonKeyFormatted, + PersonDisplayDirectoryName: results[i].PersonDisplayDirectoryName, + DirectoryNumber: results[i].DirectoryNumber, + FilePath: filePath); } return results; } @@ -791,6 +807,7 @@ internal abstract class MapLogic int? wholePercentages; List wholePercentagesCollection; Dictionary> idToWholePercentagesCollection = []; + PersonKeyFormattedIdThenWholePercentages personKeyFormattedIdThenWholePercentages; int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string message = $") {records.Count:000} join from ticks Director(ies) - C - {totalSeconds} total second(s)"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; @@ -808,7 +825,14 @@ internal abstract class MapLogic wholePercentagesCollection = idToWholePercentagesCollection[record.MappedFaceFilePath.Id.Value]; wholePercentagesCollection.Add(wholePercentages.Value); idToWholePercentagesCollection[record.MappedFaceFilePath.Id.Value].Add(wholePercentages.Value); - results.Add(new(record.PersonKeyFormatted, record.PersonDisplayDirectoryName, record.IsDefault, record.LinksCount, record.MappedFaceFilePath, record.MappedFaceFilePath.Id.Value, wholePercentages.Value)); + personKeyFormattedIdThenWholePercentages = new(PersonKeyFormatted: record.PersonKeyFormatted, + PersonDisplayDirectoryName: record.PersonDisplayDirectoryName, + IsDefault: record.IsDefault, + LinksCount: record.LinksCount, + MappedFaceFilePath: record.MappedFaceFilePath, + Id: record.MappedFaceFilePath.Id.Value, + WholePercentages: wholePercentages.Value); + results.Add(personKeyFormattedIdThenWholePercentages); } return results.AsReadOnly(); } diff --git a/Metadata/Metadata.csproj b/Metadata/Metadata.csproj index 0996706..dd07f56 100644 --- a/Metadata/Metadata.csproj +++ b/Metadata/Metadata.csproj @@ -34,10 +34,10 @@ + - \ No newline at end of file diff --git a/Metadata/Models/B_Metadata.cs b/Metadata/Models/B_Metadata.cs index fe34414..dae1ad5 100644 --- a/Metadata/Models/B_Metadata.cs +++ b/Metadata/Models/B_Metadata.cs @@ -148,7 +148,7 @@ public class B_Metadata : IMetadata DateTime? result = null; DateTime? dateTime; DateTime checkDateTime; - string dateTimeFormat = Property.Models.Stateless.IProperty.DateTimeFormat(); + string dateTimeFormat = Stateless.Methods.IMetadata.DateTimeFormat(); MetadataExtractor.Formats.Exif.ExifDirectoryBase? exifDirectoryBase = directories.OfType().FirstOrDefault(); results.Add(new DateTime(filePath.CreationTicks)); results.Add(new DateTime(filePath.LastWriteTicks)); @@ -158,7 +158,7 @@ public class B_Metadata : IMetadata results.Add(checkDateTime); else { - dateTime = Property.Models.Stateless.IProperty.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime)); + dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime)); if (dateTime is not null) results.Add(dateTime.Value); } @@ -166,7 +166,7 @@ public class B_Metadata : IMetadata results.Add(checkDateTime); else { - dateTime = Property.Models.Stateless.IProperty.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized)); + dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized)); if (dateTime is not null) results.Add(dateTime.Value); } @@ -177,7 +177,7 @@ public class B_Metadata : IMetadata } else { - dateTime = Property.Models.Stateless.IProperty.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeOriginal)); + dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeOriginal)); if (dateTime is not null) { result ??= dateTime.Value; @@ -195,7 +195,7 @@ public class B_Metadata : IMetadata } else { - dateTime = Property.Models.Stateless.IProperty.GetDateTime(dateTimeFormat, aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal)); + dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal)); if (dateTime is not null) { result ??= dateTime.Value; @@ -213,7 +213,7 @@ public class B_Metadata : IMetadata } else { - dateTime = Property.Models.Stateless.IProperty.GetDateTime(dateTimeFormat, quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated)); + dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated)); if (dateTime is not null) { result ??= dateTime.Value; @@ -231,7 +231,7 @@ public class B_Metadata : IMetadata } else { - dateTime = Property.Models.Stateless.IProperty.GetDateTime(dateTimeFormat, quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated)); + dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated)); if (dateTime is not null) { result ??= dateTime.Value; diff --git a/Metadata/Models/Stateless/Base.cs b/Metadata/Models/Stateless/Base.cs index 97ad98c..2575ec5 100644 --- a/Metadata/Models/Stateless/Base.cs +++ b/Metadata/Models/Stateless/Base.cs @@ -1,3 +1,4 @@ +using System.Globalization; using View_by_Distance.Shared.Models; namespace View_by_Distance.Metadata.Models.Stateless.Methods; @@ -47,4 +48,23 @@ internal static class Base return result; } +#pragma warning restore CA1416 + + internal static DateTime? GetDateTime(string dateTimeFormat, string? value) + { + DateTime? result; + string alternateFormat = "ddd MMM dd HH:mm:ss yyyy"; + if (value is not null && DateTime.TryParse(value, out DateTime dateTime)) + result = dateTime; + else if (value is not null && value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + result = dateTime; + else if (value is not null && value.Length == alternateFormat.Length && DateTime.TryParseExact(value, alternateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + result = dateTime; + else + result = null; + return result; + } + +#pragma warning disable CA1416 + } \ No newline at end of file diff --git a/Metadata/Models/Stateless/Methods/IMetadata.cs b/Metadata/Models/Stateless/Methods/IMetadata.cs index 81926dc..352ae9a 100644 --- a/Metadata/Models/Stateless/Methods/IMetadata.cs +++ b/Metadata/Models/Stateless/Methods/IMetadata.cs @@ -64,4 +64,14 @@ public interface IMetadata // static Dictionary GetMetadataCollection(FileInfo fileInfo, List> subFileTuples, List parseExceptions) => // Metadata.GetMetadataCollection(fileInfo, subFileTuples, parseExceptions); + string TestStatic_DateTimeFormat() => + DateTimeFormat(); + static string DateTimeFormat() => + "yyyy:MM:dd HH:mm:ss"; + + DateTime? TestStatic_GetDateTime(string dateTimeFormat, string? value) => + GetDateTime(dateTimeFormat, value); + static DateTime? GetDateTime(string dateTimeFormat, string? value) => + Base.GetDateTime(dateTimeFormat, value); + } \ No newline at end of file diff --git a/Property/Models/A_Property.cs b/Property/Models/A_Property.cs index cd037cb..62e3057 100644 --- a/Property/Models/A_Property.cs +++ b/Property/Models/A_Property.cs @@ -239,7 +239,7 @@ public class A_Property SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName); } - private void SavePropertyParallelWork(int maxDegreeOfParallelism, Shared.Models.Methods.IMetadata metadata, List exceptions, List> sourceDirectoryChanges, Container container, ReadOnlyCollection items, string message) + private void SavePropertyParallelWork(int maxDegreeOfParallelism, Shared.Models.Methods.IMetadata metadata, List exceptions, List> sourceDirectoryChanges, Container.Models.Container container, ReadOnlyCollection items, string message) { List> sourceDirectoryFileTuples = []; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; @@ -268,14 +268,14 @@ public class A_Property }); } - public void SavePropertyParallelWork(long ticks, Shared.Models.Methods.IMetadata metadata, int t, Container[] containers) + public void SavePropertyParallelWork(long ticks, Shared.Models.Methods.IMetadata metadata, int t, Container.Models.Container[] containers) { int total = 0; string message; int totalSeconds; - Container container; bool anyNullOrNoIsUniqueFileName; List exceptions = []; + Container.Models.Container container; int containersLength = containers.Length; const string outputResolution = "Original"; List> sourceDirectoryChanges = []; diff --git a/Property/Models/Stateless/IProperty.cs b/Property/Models/Stateless/IProperty.cs index d44a183..5d0ecff 100644 --- a/Property/Models/Stateless/IProperty.cs +++ b/Property/Models/Stateless/IProperty.cs @@ -55,4 +55,24 @@ public interface IProperty static (DateTime?, DateTime[], int?, string?) Get(IPropertyConfiguration propertyConfiguration, bool populateId, IMetadata? metadata, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => Property.Get(populateId, metadata, FilePath.Get(propertyConfiguration, fileHolder, index: null), isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); + TimeSpan TestStatic_GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container) => + GetThreeStandardDeviationHigh(minimum, container); + static TimeSpan GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container) => + Property.GetThreeStandardDeviationHigh(minimum, container); + + (int, List, List) TestStatic_Get(Container.Models.Container container, TimeSpan threeStandardDeviationHigh, int i) => + Get(container, threeStandardDeviationHigh, i); + static (int, List, List) Get(Container.Models.Container container, TimeSpan threeStandardDeviationHigh, int i) => + Property.Get(container, threeStandardDeviationHigh, i); + + bool TestStatic_Any(Container.Models.Container[] propertyHolderCollections) => + Any(propertyHolderCollections); + static bool Any(Container.Models.Container[] propertyHolderCollections) => + Property.Any(propertyHolderCollections); + + double TestStatic_GetStandardDeviation(List values, double average) => + GetStandardDeviation(values, average); + static double GetStandardDeviation(List values, double average) => + Property.GetStandardDeviation(values, average); + } \ No newline at end of file diff --git a/Property/Models/Stateless/Property.cs b/Property/Models/Stateless/Property.cs index b294a1f..ee90854 100644 --- a/Property/Models/Stateless/Property.cs +++ b/Property/Models/Stateless/Property.cs @@ -366,4 +366,112 @@ internal partial class Property return new(property?.DateTimeOriginal, dateTimes, property?.Id, message); } + internal static double GetStandardDeviation(List values, double average) + { + double result = 0; + if (values.Count == 0) + throw new Exception("Collection must have at least one value!"); + double sum = values.Sum(l => (l - average) * (l - average)); + result = Math.Sqrt(sum / values.Count); + return result; + } + + private static long GetThreeStandardDeviationHigh(ref List ticksCollection, long min) + { + long result; + ticksCollection = (from l in ticksCollection select l - min).ToList(); + double sum = ticksCollection.Sum(); + double average = sum / ticksCollection.Count; + double standardDeviation = GetStandardDeviation(ticksCollection, average); + result = (long)Math.Ceiling(average + min + (standardDeviation * 3)); + return result; + } + + internal static TimeSpan GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container) + { + TimeSpan result; + DateTime? minimumDateTime; + List ticksCollection = []; + foreach (Shared.Models.Item item in container.Items) + { + if (item.Property is null) + continue; + minimumDateTime = Shared.Models.Methods.IProperty.GetMinimumDateTime(item.Property); + if (minimumDateTime is null) + continue; + ticksCollection.Add(minimumDateTime.Value.Ticks); + } + long threeStandardDeviationHigh; + long min; + if (ticksCollection.Count == 0) + min = 0; + else + min = ticksCollection.Min(); + if (ticksCollection.Count < minimum) + threeStandardDeviationHigh = long.MaxValue; + else + threeStandardDeviationHigh = GetThreeStandardDeviationHigh(ref ticksCollection, min); + result = new TimeSpan(threeStandardDeviationHigh - min); + return result; + } + + internal static (int, List, List) Get(Container.Models.Container container, TimeSpan threeStandardDeviationHigh, int i) + { + List results = []; + int j = i; + long? ticks; + TimeSpan timeSpan; + Shared.Models.Item item; + DateTime? minimumDateTime; + Shared.Models.Item nextItem; + DateTime? nextMinimumDateTime; + List dateTimes = []; + for (; j < container.Items.Count; j++) + { + ticks = null; + item = container.Items[j]; + if (item.Property is null) + continue; + minimumDateTime = Shared.Models.Methods.IProperty.GetMinimumDateTime(item.Property); + if (minimumDateTime is null) + continue; + for (int k = j + 1; k < container.Items.Count; k++) + { + nextItem = container.Items[k]; + if (nextItem.Property is null) + continue; + nextMinimumDateTime = Shared.Models.Methods.IProperty.GetMinimumDateTime(nextItem.Property); + if (nextMinimumDateTime is null) + continue; + ticks = nextMinimumDateTime.Value.Ticks; + break; + } + results.Add(item); + dateTimes.Add(minimumDateTime.Value); + if (ticks.HasValue) + { + timeSpan = new(ticks.Value - minimumDateTime.Value.Ticks); + if (timeSpan > threeStandardDeviationHigh) + break; + } + } + return new(j, dateTimes, results); + } + + internal static bool Any(Container.Models.Container[] containers) + { + bool result = false; + foreach (Container.Models.Container container in containers) + { + if (container.Items.Count == 0) + continue; + if ((from l in container.Items where l.Any() select true).Any()) + { + result = true; + break; + } + } + return result; + } + } \ No newline at end of file diff --git a/Property/Property.csproj b/Property/Property.csproj index 7b27266..4b5560c 100644 --- a/Property/Property.csproj +++ b/Property/Property.csproj @@ -46,6 +46,7 @@ + \ No newline at end of file diff --git a/Resize/Models/_C_Resize.cs b/Resize/Models/_C_Resize.cs index bd523ae..40a1c83 100644 --- a/Resize/Models/_C_Resize.cs +++ b/Resize/Models/_C_Resize.cs @@ -315,7 +315,7 @@ public class C_Resize throw new Exception(); } - public void SaveResizedSubfile(Configuration configuration, string outputResolution, string cResultsFullGroupDirectory, List> subFileTuples, Item item, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize) + public void SaveResizedSubfile(Property.Models.Configuration configuration, string outputResolution, string cResultsFullGroupDirectory, List> subFileTuples, Item item, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize) { if (mappingFromItem.ResizedFileHolder is null) throw new NullReferenceException(nameof(mappingFromItem.ResizedFileHolder)); @@ -462,7 +462,7 @@ public class C_Resize } } - public Dictionary GetResizeKeyValuePairs(Configuration configuration, string cResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem) + public Dictionary GetResizeKeyValuePairs(Property.Models.Configuration configuration, string cResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem) { Dictionary? results; string json; diff --git a/Shared/Models/FilePair.cs b/Shared/Models/FilePair.cs index 44e057a..3fbed92 100644 --- a/Shared/Models/FilePair.cs +++ b/Shared/Models/FilePair.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; namespace View_by_Distance.Shared.Models; -internal record FilePair(string Path, +public record FilePair(string Path, bool IsUnique, bool? IsNotUniqueAndNeedsReview, List Collection, @@ -20,6 +20,6 @@ internal record FilePair(string Path, [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(FilePair))] -internal partial class FilePairSourceGenerationContext : JsonSerializerContext +public partial class FilePairSourceGenerationContext : JsonSerializerContext { } \ No newline at end of file diff --git a/Shared/Models/Methods/IContainer.cs b/Shared/Models/Methods/IContainer.cs deleted file mode 100644 index 3b7a13e..0000000 --- a/Shared/Models/Methods/IContainer.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace View_by_Distance.Shared.Models.Methods; - -public interface IContainer : Stateless.Methods.IContainer -{ // ... - - // - -} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/Container.cs b/Shared/Models/Stateless/Methods/Container.cs deleted file mode 100644 index 057499d..0000000 --- a/Shared/Models/Stateless/Methods/Container.cs +++ /dev/null @@ -1,302 +0,0 @@ -using System.Collections.ObjectModel; -using System.Text.Json; - -namespace View_by_Distance.Shared.Models.Stateless.Methods; - -internal abstract class Container -{ - - private record FilePair(bool IsUnique, List Collection, FilePath FilePath, Models.Item Item) { } - - internal static DateTime[] GetContainerDateTimes(ReadOnlyCollection items) - { - DateTime[] results; - long containerMinimumTicks = (from l in items select l.FilePath.LastWriteTicks).Min(); - long containerMaximumTicks = (from l in items select l.FilePath.LastWriteTicks).Max(); - results = [new(containerMinimumTicks), new(containerMaximumTicks)]; - return results; - } - - internal static ReadOnlyCollection GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, Models.Container container) - { - List results = []; - foreach (Models.Item item in container.Items) - { - if (!item.IsValidImageFormatExtension || propertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered)) - continue; - results.Add(item); - } - return container.Items.Count == results.Count ? container.Items : results.AsReadOnly(); - } - - private static List GetFilePairs(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string aPropertySingletonDirectory, ReadOnlyCollection> filePathsCollection) - { - int renamed; - const bool useCeilingAverage = true; - List? filePairs = null; - ReadOnlyCollection? jsonFilesCollection = null; - IReadOnlyDictionary>? compareFileNamesToFiles = null; - IReadOnlyDictionary> fileNamesToFiles = XDirectory.GetFilesKeyValuePairs(filePathsCollection); - for (int i = 0; i < short.MaxValue; i++) - { - renamed = 0; - jsonFilesCollection = IDirectory.GetFilesCollection(aPropertySingletonDirectory, directorySearchFilter, extension, useCeilingAverage); - compareFileNamesToFiles = XDirectory.GetFilesKeyValuePairs(jsonFilesCollection); - renamed += XDirectory.LookForAbandoned(jsonFilesCollection, fileNamesToFiles, extension); - filePairs = XDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, extension, compareFileNamesToFiles); - renamed += XDirectory.MaybeMove(propertyConfiguration, filePairs, aPropertySingletonDirectory, extension); - if (renamed == 0) - break; - if (i > 10) - throw new NotImplementedException(); - } - if (filePairs is null || jsonFilesCollection is null || compareFileNamesToFiles is null) - throw new NullReferenceException(nameof(filePairs)); - return filePairs; - } - - private static Models.Property? GetProperty(Models.FilePair filePair) - { - Models.Property? result; - if (filePair.Match is null) - result = null; - else - { - string json = File.ReadAllText(filePair.Match); - if (string.IsNullOrEmpty(json)) - result = null; - else - result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property); - } - return result; - } - - private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, string fullFileName) - { - string[] segments = directory.Split(cei.Combined); - string? checkDirectory = segments.Length == 1 ? - Path.Combine(segments[0], $"{cei.Combined[2..]}") : - segments.Length == 2 ? - $"{segments[0]}{cei.Combined[2..]}{segments[1]}" : - null; - if (checkDirectory is not null && Directory.Exists(checkDirectory)) - { - string checkFile = Path.Combine(checkDirectory, fileName); - if (File.Exists(checkFile)) - File.Move(checkFile, fullFileName); - } - } - - private static void ParallelFor(IDlibDotNet? dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string extension, int rootDirectoryLength, ReadOnlyDictionary? splatNineIdentifiers, Models.FilePair filePair, List results) - { - dlibDotNet?.Tick(); - bool abandoned = false; - Models.FileHolder sourceDirectoryFileHolder; - Models.Property? property = GetProperty(filePair); - Models.FileHolder imageFileHolder = IFileHolder.Get(filePair.Path); - FilePath filePath = FilePath.Get(propertyConfiguration, imageFileHolder, index: null); - bool? fileSizeChanged = property is not null ? property.FileSize != filePath.Length : null; - bool isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(filePath.ExtensionLowered); - bool? shouldIgnore = property is null || property.Keywords is null ? null : propertyConfiguration.IgnoreRulesKeyWords.Any(l => property.Keywords.Contains(l)); - bool? isArchive = filePath.Id is null || splatNineIdentifiers is null ? null : splatNineIdentifiers.TryGetValue(filePath.Id.Value, out Identifier? identifier); - if (shouldIgnore is not null) - { - if (shouldIgnore.Value) - { - FileInfo fileInfo = new(filePath.FullName); - if (!fileInfo.Attributes.HasFlag(FileAttributes.Hidden)) - File.SetAttributes(imageFileHolder.FullName, FileAttributes.Hidden); - } - if (filePath.HasIgnoreKeyword is not null && filePath.HasIgnoreKeyword.Value != shouldIgnore.Value) - { - if (filePath.DirectoryFullPath.Contains("Results") && filePath.DirectoryFullPath.Contains("Resize")) - File.Delete(filePath.FullName); - else - throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>"); - } - } - string relativePath = IPath.GetRelativePath(filePair.Path, rootDirectoryLength, forceExtensionToLower: true); - bool? lastWriteTimeChanged = property is not null ? propertyConfiguration.PropertiesChangedForProperty || property.LastWriteTime.Ticks != filePath.LastWriteTicks : null; - if (filePair.Match is not null) - sourceDirectoryFileHolder = IFileHolder.Get(filePair.Match); - else if (!filePair.IsUnique) - sourceDirectoryFileHolder = IFileHolder.Get(Path.GetFullPath(string.Concat(aPropertySingletonDirectory, relativePath, extension))); - else - { - string fileName = Path.GetFileName(filePair.Path); - CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); - string directory = Path.Combine(aPropertySingletonDirectory, cei.Combined); - string jsonFileName = $"{fileName}{extension}"; - string fullFileName = Path.Combine(directory, jsonFileName); - MoveIf(jsonFileName, cei, directory, fullFileName); - sourceDirectoryFileHolder = IFileHolder.Get(fullFileName); - } - if (sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && filePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks) - { - File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePath.LastWriteTicks)); - File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value); - } - Models.Item item = Models.Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, property, abandoned, fileSizeChanged, lastWriteTimeChanged); - lock (results) - results.Add(new(filePair.IsUnique, filePair.Collection, filePath, item)); - } - - private static List GetFilePairs(IDlibDotNet? dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, string directorySearchFilter) - { - List results = []; - const string extension = ".json"; - int maxDegreeOfParallelism = Environment.ProcessorCount; - int filesCollectionDirectoryLength = filesCollectionDirectory.Length; - ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; - List filePairs = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, aPropertySingletonDirectory, filePathsCollection); - _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, extension, filesCollectionDirectoryLength, splatNineIdentifiers, filePairs[i], results)); - return results; - } - - private static (int, Models.Container[]) GetContainers(IDlibDotNet? dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, string directorySearchFilter) - { - List results = []; - string directory; - List? items; - Models.Container container; - List directories = []; - Dictionary> directoryToItems = []; - foreach (ReadOnlyCollection filePaths in filePathsCollection) - { - if (filePaths.Count == 0) - continue; - directory = filePaths[0].DirectoryFullPath; - if (directory is null) - continue; - if (!directories.Contains(directory)) - directories.Add(directory); - if (!directoryToItems.TryGetValue(directory, out items)) - { - directoryToItems.Add(directory, []); - if (!directoryToItems.TryGetValue(directory, out items)) - throw new Exception(); - } - } - List filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, splatNineIdentifiers, filePathsCollection, directorySearchFilter); - foreach (FilePair filePair in filePairs) - { - if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items)) - { - directoryToItems.Add(filePair.FilePath.DirectoryFullPath, []); - if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items)) - throw new Exception(); - } - items.Add(filePair.Item); - } - foreach (KeyValuePair> keyValuePair in directoryToItems) - { - if (keyValuePair.Value.Count == 0) - continue; - container = new(keyValuePair.Key, new(keyValuePair.Value)); - results.Add(container); - } - return (filePairs.Count, results.ToArray()); - } - - internal static ReadOnlyCollection GetContainers(IDlibDotNet dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection) - { - Models.Container[] results; - const string directorySearchFilter = "*"; - (_, results) = GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, splatNineIdentifiers, filePathsCollection, directorySearchFilter); - return results.AsReadOnly(); - } - - internal static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) - { - int count; - Models.Container[] results; - IDlibDotNet? dlibDotNet = null; - const bool useCeilingAverage = true; - const string fileSearchFilter = "*"; - const string directorySearchFilter = "*"; - ReadOnlyCollection filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage); - ReadOnlyCollection> filePathsCollection = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection); - (count, results) = GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, propertyConfiguration.RootDirectory, splatNineIdentifiers, filePathsCollection, directorySearchFilter); - return (count, results); - } - - internal static List GetFilteredDistinctIds(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) - { - List results = []; - ReadOnlyCollection filteredItems; - foreach (Models.Container container in readOnlyContainers) - { - if (container.Items.Count == 0) - continue; - filteredItems = GetValidImageItems(propertyConfiguration, container); - if (filteredItems.Count == 0) - continue; - foreach (Models.Item item in filteredItems) - { - if (item.Property?.Id is null || item.ResizedFileHolder is null) - continue; - if (results.Contains(item.Property.Id.Value)) - continue; - results.Add(item.Property.Id.Value); - } - } - return results; - } - - internal static List GetFilteredDistinctFileNameFirstSegments(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) - { - List results = []; - ReadOnlyCollection filteredItems; - foreach (Models.Container container in readOnlyContainers) - { - if (container.Items.Count == 0) - continue; - filteredItems = GetValidImageItems(propertyConfiguration, container); - if (filteredItems.Count == 0) - continue; - foreach (Models.Item item in filteredItems) - { - if (item.Property?.Id is null || item.ResizedFileHolder is null) - continue; - if (results.Contains(item.FilePath.FileNameFirstSegment)) - continue; - results.Add(item.FilePath.FileNameFirstSegment); - } - } - return results; - } - - internal static ReadOnlyCollection GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) - { - List results = []; - List distinct = []; - ReadOnlyCollection filteredItems; - foreach (Models.Container container in containers) - { - if (container.Items.Count == 0) - continue; - if (!filterItems) - filteredItems = container.Items; - else - { - filteredItems = GetValidImageItems(propertyConfiguration, container); - if (filteredItems.Count == 0) - continue; - } - foreach (Models.Item item in filteredItems) - { - if (item.Property?.Id is null || item.ResizedFileHolder is null) - continue; - if (distinctItems) - { - if (distinct.Contains(item.Property.Id.Value)) - continue; - distinct.Add(item.Property.Id.Value); - } - results.Add(item); - } - } - return results.AsReadOnly(); - } - -} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IContainer.cs b/Shared/Models/Stateless/Methods/IContainer.cs deleted file mode 100644 index ddf82e2..0000000 --- a/Shared/Models/Stateless/Methods/IContainer.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Collections.ObjectModel; - -namespace View_by_Distance.Shared.Models.Stateless.Methods; - -public interface IContainer -{ - - DateTime[] TestStatic_GetContainerDateTimes(ReadOnlyCollection items) => - GetContainerDateTimes(items); - static DateTime[] GetContainerDateTimes(ReadOnlyCollection items) => - Container.GetContainerDateTimes(items); - - ReadOnlyCollection TestStatic_GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, Models.Container container) => - GetValidImageItems(propertyConfiguration, container); - static ReadOnlyCollection GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, Models.Container container) => - Container.GetValidImageItems(propertyConfiguration, container); - - (int, Models.Container[]) TestStatic_GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => - GetContainers(propertyConfiguration, aPropertySingletonDirectory); - static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => - GetContainers(propertyConfiguration, null, aPropertySingletonDirectory); - - (int, Models.Container[]) TestStatic_GetContainers(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) => - GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory); - static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) => - Container.GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory); - - ReadOnlyCollection TestStatic_GetContainers(IDlibDotNet dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection) => - GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, splatNineIdentifiers, filePathsCollection); - static ReadOnlyCollection GetContainers(IDlibDotNet dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection) => - Container.GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, splatNineIdentifiers, filePathsCollection); - - List TestStatic_GetFilteredDistinctIds(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => - GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); - static List GetFilteredDistinctIds(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => - Container.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); - - List TestStatic_GetFilteredDistinctFileNameFirstSegments(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => - GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); - static List GetFilteredDistinctFileNameFirstSegments(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => - Container.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); - - ReadOnlyCollection TestStatic_GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) => - GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems); - static ReadOnlyCollection GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) => - Container.GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems); - -} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IDirectory.cs b/Shared/Models/Stateless/Methods/IDirectory.cs index ed4c8ca..52f24fe 100644 --- a/Shared/Models/Stateless/Methods/IDirectory.cs +++ b/Shared/Models/Stateless/Methods/IDirectory.cs @@ -50,4 +50,14 @@ public interface IDirectory static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection) => XDirectory.GetFilePathCollections(propertyConfiguration, filesCollection); + List TestStatic_GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) => + GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles); + static List GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) => + XDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles); + + int TestStatic_MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List filePairs, string jsonGroupDirectory, string extension) => + MaybeMove(propertyConfiguration, filePairs, jsonGroupDirectory, extension); + static int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List filePairs, string jsonGroupDirectory, string extension) => + XDirectory.MaybeMove(propertyConfiguration, filePairs, jsonGroupDirectory, extension); + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IDlibDotNet.cs b/Shared/Models/Stateless/Methods/IDlibDotNet.cs index ac13a76..cbb593f 100644 --- a/Shared/Models/Stateless/Methods/IDlibDotNet.cs +++ b/Shared/Models/Stateless/Methods/IDlibDotNet.cs @@ -4,5 +4,7 @@ public interface IDlibDotNet { void Tick(); + (string, string) GetResultsFullGroupDirectories(); + (string, string, string, string) GetResultsFullGroupDirectories(string outputResolution); } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IId.cs b/Shared/Models/Stateless/Methods/IId.cs index 985cf53..92ea55c 100644 --- a/Shared/Models/Stateless/Methods/IId.cs +++ b/Shared/Models/Stateless/Methods/IId.cs @@ -23,29 +23,16 @@ public interface IId static string GetPaddedId(Properties.IPropertyConfiguration propertyConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) => Id.GetPaddedId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal, index); - string TestStatic_GetIgnoreFullPath(FilePath filePath, Models.FileHolder fileHolder) => - GetIgnoreFullPath(filePath, fileHolder); - static string GetIgnoreFullPath(FilePath filePath, Models.FileHolder fileHolder) => - fileHolder.DirectoryFullPath is null ? - throw new NotSupportedException() : - filePath.Id > -1 ? - fileHolder.NameWithoutExtension[^1] == '9' ? - Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension[..^1]}8{fileHolder.ExtensionLowered}") : - throw new NotSupportedException("High") : - fileHolder.NameWithoutExtension[^1] == '1' ? - Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension[..^1]}2{fileHolder.ExtensionLowered}") : - throw new NotSupportedException("Low"); - bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(Properties.IPropertyConfiguration propertyConfiguration, string fileNameFirstSegment) => NameWithoutExtensionIsIntelligentIdFormat(propertyConfiguration, fileNameFirstSegment); static bool NameWithoutExtensionIsIntelligentIdFormat(Properties.IPropertyConfiguration propertyConfiguration, string fileNameFirstSegment) => - fileNameFirstSegment.Length - 1 == propertyConfiguration.IntMinValueLength && fileNameFirstSegment[^1] is '1' or '2' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber); + fileNameFirstSegment.Length - 1 == propertyConfiguration.IntMinValueLength && fileNameFirstSegment[^1] is '1' or '2' or '3' or '7' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber); bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(Properties.IPropertyConfiguration propertyConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) => NameWithoutExtensionIsPaddedIntelligentIdFormat(propertyConfiguration, sortOrderOnlyLengthIndex, fileNameFirstSegment); static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(Properties.IPropertyConfiguration propertyConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) => fileNameFirstSegment.Length == propertyConfiguration.IntMinValueLength + sortOrderOnlyLengthIndex + 1 - && fileNameFirstSegment[^1] is '1' or '2' or '8' or '9' + && fileNameFirstSegment[^1] is '1' or '2' or '3' or '7' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber); bool TestStatic_NameWithoutExtensionIsIdFormat(Properties.IPropertyConfiguration propertyConfiguration, Models.FileHolder fileHolder) => @@ -58,4 +45,19 @@ public interface IId static int GetDeterministicHashCode(byte[] value) => Id.GetDeterministicHashCode(value); + int TestStatic_GetHasIgnoreKeyword(FilePath filePath) => + GetHasIgnoreKeyword(filePath); + static int GetHasIgnoreKeyword(FilePath filePath) => + Id.GetHasIgnoreKeyword(filePath); + + int TestStatic_GetMissingDateTimeOriginal(FilePath filePath) => + GetMissingDateTimeOriginal(filePath); + static int GetMissingDateTimeOriginal(FilePath filePath) => + Id.GetMissingDateTimeOriginal(filePath); + + int TestStatic_GetHasDateTimeOriginal(FilePath filePath) => + GetHasDateTimeOriginal(filePath); + static int GetHasDateTimeOriginal(FilePath filePath) => + Id.GetHasDateTimeOriginal(filePath); + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IPath.cs b/Shared/Models/Stateless/Methods/IPath.cs index 6ad6a96..fd28965 100644 --- a/Shared/Models/Stateless/Methods/IPath.cs +++ b/Shared/Models/Stateless/Methods/IPath.cs @@ -72,4 +72,9 @@ public interface IPath static ReadOnlyDictionary>> GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) => XPath.GetKeyValuePairs(propertyConfiguration, resultsFullGroupDirectory, jsonGroups); + byte TestStatic_GetEnum(FilePath filePath) => + GetEnum(filePath); + static byte GetEnum(FilePath filePath) => + XPath.GetEnum(filePath); + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IProperty.cs b/Shared/Models/Stateless/Methods/IProperty.cs index 47e96ef..6b96d76 100644 --- a/Shared/Models/Stateless/Methods/IProperty.cs +++ b/Shared/Models/Stateless/Methods/IProperty.cs @@ -23,11 +23,6 @@ public interface IProperty static string GetDiffRootDirectory(string diffPropertyDirectory) => Property.GetDiffRootDirectory(diffPropertyDirectory); - bool TestStatic_Any(Models.Container[] propertyHolderCollections) => - Any(propertyHolderCollections); - static bool Any(Models.Container[] propertyHolderCollections) => - Property.Any(propertyHolderCollections); - (bool?, string[]) TestStatic_IsWrongYear(string[] segments, string year) => IsWrongYear(segments, year); static (bool?, string[]) IsWrongYear(string[] segments, string year) => @@ -43,21 +38,6 @@ public interface IProperty static List GetDateTimes(Models.Property property) => Property.GetDateTimes(property.CreationTime, property.LastWriteTime, property.DateTime, property.DateTimeDigitized, property.DateTimeFromName, property.DateTimeOriginal, property.GPSDateStamp); - double TestStatic_GetStandardDeviation(List values, double average) => - GetStandardDeviation(values, average); - static double GetStandardDeviation(List values, double average) => - Property.GetStandardDeviation(values, average); - - TimeSpan TestStatic_GetThreeStandardDeviationHigh(int minimum, Models.Container container) => - GetThreeStandardDeviationHigh(minimum, container); - static TimeSpan GetThreeStandardDeviationHigh(int minimum, Models.Container container) => - Property.GetThreeStandardDeviationHigh(minimum, container); - - (int, List, List) TestStatic_Get(Models.Container container, TimeSpan threeStandardDeviationHigh, int i) => - Get(container, threeStandardDeviationHigh, i); - static (int, List, List) Get(Models.Container container, TimeSpan threeStandardDeviationHigh, int i) => - Property.Get(container, threeStandardDeviationHigh, i); - List TestStatic_GetDateTimes(DateTime creationTime, DateTime lastWriteTime, DateTime? dateTime, DateTime? dateTimeDigitized, DateTime? dateTimeFromName, DateTime? dateTimeOriginal, DateTime? gpsDateStamp) => GetDateTimes(creationTime, lastWriteTime, dateTime, dateTimeDigitized, dateTimeFromName, dateTimeOriginal, gpsDateStamp); static List GetDateTimes(DateTime creationTime, DateTime lastWriteTime, DateTime? dateTime, DateTime? dateTimeDigitized, DateTime? dateTimeFromName, DateTime? dateTimeOriginal, DateTime? gpsDateStamp) => diff --git a/Shared/Models/Stateless/Methods/Id.cs b/Shared/Models/Stateless/Methods/Id.cs index be19d22..28a901e 100644 --- a/Shared/Models/Stateless/Methods/Id.cs +++ b/Shared/Models/Stateless/Methods/Id.cs @@ -28,13 +28,22 @@ internal abstract class Id _ = results.Append(intelligentId[i]); _ = results.Append(intelligentId[^3]).Append(intelligentId[^2]); result = int.Parse(results.ToString()); - if (intelligentId[^1] is '1' or '2') + if (intelligentId[^1] is '1' or '2' or '3') result *= -1; - else if (intelligentId[^1] is not '9' and not '8') + else if (intelligentId[^1] is not '9' and not '8' and not '7') throw new NotSupportedException(); return result; } + internal static int GetHasIgnoreKeyword(FilePath filePath) => + filePath.Id > -1 ? 8 : 2; + + internal static int GetMissingDateTimeOriginal(FilePath filePath) => + filePath.Id > -1 ? 7 : 3; + + internal static int GetHasDateTimeOriginal(FilePath filePath) => + filePath.Id > -1 ? 9 : 1; + internal static string GetIntelligentId(Properties.IPropertyConfiguration propertyConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) { string result; @@ -48,12 +57,12 @@ internal abstract class Id List resultAllInOneSubdirectoryChars = []; if (id > -1) { - key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : 9; + key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : hasDateTimeOriginal is not null && hasDateTimeOriginal.Value ? 9 : 7; value = id.ToString().PadLeft(propertyConfiguration.IntMinValueLength, '0'); } else { - key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : 1; + key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : hasDateTimeOriginal is not null && hasDateTimeOriginal.Value ? 1 : 3; value = id.ToString()[1..].PadLeft(propertyConfiguration.IntMinValueLength, '0'); } for (int i = value.Length - propertyConfiguration.ResultAllInOneSubdirectoryLength - 1; i > -1; i--) diff --git a/Shared/Models/Stateless/Methods/Property.cs b/Shared/Models/Stateless/Methods/Property.cs index 97383af..8d51e6e 100644 --- a/Shared/Models/Stateless/Methods/Property.cs +++ b/Shared/Models/Stateless/Methods/Property.cs @@ -171,112 +171,4 @@ internal abstract class Property return result; } - internal static double GetStandardDeviation(List values, double average) - { - double result = 0; - if (values.Count == 0) - throw new Exception("Collection must have at least one value!"); - double sum = values.Sum(l => (l - average) * (l - average)); - result = Math.Sqrt(sum / values.Count); - return result; - } - - private static long GetThreeStandardDeviationHigh(ref List ticksCollection, long min) - { - long result; - ticksCollection = (from l in ticksCollection select l - min).ToList(); - double sum = ticksCollection.Sum(); - double average = sum / ticksCollection.Count; - double standardDeviation = GetStandardDeviation(ticksCollection, average); - result = (long)Math.Ceiling(average + min + (standardDeviation * 3)); - return result; - } - - internal static TimeSpan GetThreeStandardDeviationHigh(int minimum, Models.Container container) - { - TimeSpan result; - DateTime? minimumDateTime; - List ticksCollection = []; - foreach (Models.Item item in container.Items) - { - if (item.Property is null) - continue; - minimumDateTime = GetMinimumDateTime(item.Property); - if (minimumDateTime is null) - continue; - ticksCollection.Add(minimumDateTime.Value.Ticks); - } - long threeStandardDeviationHigh; - long min; - if (ticksCollection.Count == 0) - min = 0; - else - min = ticksCollection.Min(); - if (ticksCollection.Count < minimum) - threeStandardDeviationHigh = long.MaxValue; - else - threeStandardDeviationHigh = GetThreeStandardDeviationHigh(ref ticksCollection, min); - result = new TimeSpan(threeStandardDeviationHigh - min); - return result; - } - - internal static (int, List, List) Get(Models.Container container, TimeSpan threeStandardDeviationHigh, int i) - { - List results = []; - int j = i; - long? ticks; - Models.Item item; - TimeSpan timeSpan; - Models.Item nextItem; - DateTime? minimumDateTime; - DateTime? nextMinimumDateTime; - List dateTimes = []; - for (; j < container.Items.Count; j++) - { - ticks = null; - item = container.Items[j]; - if (item.Property is null) - continue; - minimumDateTime = GetMinimumDateTime(item.Property); - if (minimumDateTime is null) - continue; - for (int k = j + 1; k < container.Items.Count; k++) - { - nextItem = container.Items[k]; - if (nextItem.Property is null) - continue; - nextMinimumDateTime = GetMinimumDateTime(nextItem.Property); - if (nextMinimumDateTime is null) - continue; - ticks = nextMinimumDateTime.Value.Ticks; - break; - } - results.Add(item); - dateTimes.Add(minimumDateTime.Value); - if (ticks.HasValue) - { - timeSpan = new(ticks.Value - minimumDateTime.Value.Ticks); - if (timeSpan > threeStandardDeviationHigh) - break; - } - } - return new(j, dateTimes, results); - } - - internal static bool Any(Models.Container[] containers) - { - bool result = false; - foreach (Models.Container container in containers) - { - if (container.Items.Count == 0) - continue; - if ((from l in container.Items where l.Any() select true).Any()) - { - result = true; - break; - } - } - return result; - } - } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/XDirectory.cs b/Shared/Models/Stateless/Methods/XDirectory.cs index 8bae115..fd568ec 100644 --- a/Shared/Models/Stateless/Methods/XDirectory.cs +++ b/Shared/Models/Stateless/Methods/XDirectory.cs @@ -33,6 +33,7 @@ internal abstract partial class XDirectory internal static ReadOnlyCollection GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) { List results = []; + string[] files; if (!fileSearchFilter.Contains('*')) fileSearchFilter = string.Concat('*', fileSearchFilter); if (!directorySearchFilter.Contains('*')) @@ -44,7 +45,12 @@ internal abstract partial class XDirectory foreach (string innerDirectory in directories) { try - { results.Add(Directory.GetFiles(innerDirectory, fileSearchFilter, SearchOption.AllDirectories)); } + { + files = Directory.GetFiles(innerDirectory, fileSearchFilter, SearchOption.AllDirectories); + if (files.Length == 0) + continue; + results.Add(files); + } catch (UnauthorizedAccessException) { continue; } } @@ -62,77 +68,6 @@ internal abstract partial class XDirectory return results; } - internal static IReadOnlyDictionary> GetFilesKeyValuePairs(ReadOnlyCollection filesCollection) - { - Dictionary> results = []; - string fileName; - List? collection; - foreach (string[] files in filesCollection) - { - foreach (string file in files) - { - fileName = Path.GetFileName(file); - if (!results.TryGetValue(fileName, out collection)) - { - results.Add(fileName, []); - if (!results.TryGetValue(fileName, out collection)) - throw new Exception(); - } - collection.Add(file); - } - } - return results; - } - - internal static IReadOnlyDictionary> GetFilesKeyValuePairs(ReadOnlyCollection> filePathsCollection) - { - Dictionary> results = []; - List? collection; - foreach (ReadOnlyCollection filePaths in filePathsCollection) - { - foreach (FilePath filePath in filePaths) - { - if (!results.TryGetValue(filePath.Name, out collection)) - { - results.Add(filePath.Name, []); - if (!results.TryGetValue(filePath.Name, out collection)) - throw new Exception(); - } - collection.Add(filePath.FullName); - } - } - return results; - } - - internal static int LookForAbandoned(ReadOnlyCollection jsonFilesCollection, IReadOnlyDictionary> fileNamesToFiles, string extension) - { - string fileName; - string fileNameWith; - List? collection; - string fileNameUpperExtension; - int length = extension.Length; - List renameCollection = []; - foreach (string[] files in jsonFilesCollection) - { - foreach (string file in files) - { - fileNameWith = Path.GetFileName(file); - if (fileNameWith.Length < length || !fileNameWith.EndsWith(extension)) - throw new Exception(); - fileName = fileNameWith[..^length]; - if (!fileNamesToFiles.TryGetValue(fileName, out collection)) - { - fileNameUpperExtension = string.Concat(Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName).ToUpper()); - if (fileName == fileNameUpperExtension || !fileNamesToFiles.TryGetValue(fileNameUpperExtension, out collection)) - renameCollection.Add(file); - } - } - } - if (renameCollection.Count > 0) - IDirectory.MoveFiles(renameCollection, "{}", "{abd}"); - return renameCollection.Count; - } - private static bool GetIsNotUniqueAndNeedsReview(string file, List collection) { bool result = false; @@ -175,13 +110,15 @@ internal abstract partial class XDirectory return result; } - internal static List GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, string extension, IReadOnlyDictionary> compareFileNamesToFiles) + internal static List GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) { List results = []; string? match; + FilePair filePair; bool uniqueFileName; List? collection; bool? isNotUniqueAndNeedsReview; + string fileNameWithoutExtensionMinusOne; foreach (ReadOnlyCollection filePaths in filePathsCollection) { foreach (FilePath filePath in filePaths) @@ -189,25 +126,43 @@ internal abstract partial class XDirectory isNotUniqueAndNeedsReview = null; if (propertyConfiguration.IgnoreExtensions.Contains(filePath.ExtensionLowered)) continue; - if (!fileNamesToFiles.TryGetValue(filePath.Name, out collection)) + fileNameWithoutExtensionMinusOne = filePath.NameWithoutExtension[..^1]; + if (!fileNamesToFiles.TryGetValue(fileNameWithoutExtensionMinusOne, out collection)) throw new Exception(); uniqueFileName = collection.Count == 1; if (!uniqueFileName) isNotUniqueAndNeedsReview = GetIsNotUniqueAndNeedsReview(filePath.FullName, collection); - if (!compareFileNamesToFiles.TryGetValue(string.Concat(filePath.Name, extension), out collection)) - results.Add(new(filePath.FullName, uniqueFileName, isNotUniqueAndNeedsReview, [], null)); + if (!compareFileNamesToFiles.TryGetValue(fileNameWithoutExtensionMinusOne, out collection)) + filePair = new(Path: filePath.FullName, + IsUnique: uniqueFileName, + IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview, + Collection: [], + Match: null); else { if (collection.Count == 0) - results.Add(new(filePath.FullName, uniqueFileName, isNotUniqueAndNeedsReview, collection, null)); + filePair = new(Path: filePath.FullName, + IsUnique: uniqueFileName, + IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview, + Collection: collection, + Match: null); else if (uniqueFileName && collection.Count == 1) - results.Add(new(filePath.FullName, uniqueFileName, isNotUniqueAndNeedsReview, collection, collection.First())); + filePair = new(Path: filePath.FullName, + IsUnique: uniqueFileName, + IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview, + Collection: collection, + Match: collection.First()); else { match = GetMatch(filePath.FullName, collection); - results.Add(new(filePath.FullName, uniqueFileName, isNotUniqueAndNeedsReview, collection, match)); + filePair = new(Path: filePath.FullName, + IsUnique: uniqueFileName, + IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview, + Collection: collection, + Match: match); } } + results.Add(filePair); } } return results; @@ -458,6 +413,7 @@ internal abstract partial class XDirectory { List results = []; FileInfo fileInfo; + List distinctExtensions = []; foreach ((FilePath filePath, string to) in toDoCollection) { tick?.Invoke(); @@ -471,6 +427,8 @@ internal abstract partial class XDirectory results.Add(filePath.NameWithoutExtension); try { + if (!distinctExtensions.Contains(filePath.ExtensionLowered)) + distinctExtensions.Add(filePath.ExtensionLowered); if (move || moveBack) File.Move(filePath.FullName, to); else diff --git a/Shared/Models/Stateless/Methods/XPath.cs b/Shared/Models/Stateless/Methods/XPath.cs index 20d77cb..5fa0101 100644 --- a/Shared/Models/Stateless/Methods/XPath.cs +++ b/Shared/Models/Stateless/Methods/XPath.cs @@ -292,6 +292,9 @@ internal abstract class XPath return result; } + internal static byte GetEnum(FilePath filePath) => + GetEnum(filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal); + internal static CombinedEnumAndIndex GetCombinedEnumAndIndex(int resultAllInOneSubdirectoryLength, FilePath filePath, string fileName) { CombinedEnumAndIndex result;