diff --git a/Distance/Models/_E_Distance.cs b/Distance/Models/_E_Distance.cs index e1ad047..f133b49 100644 --- a/Distance/Models/_E_Distance.cs +++ b/Distance/Models/_E_Distance.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Text.Json; using View_by_Distance.Distance.Models.Stateless; using View_by_Distance.FaceRecognitionDotNet; +using View_by_Distance.Property.Models.Stateless; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Methods; @@ -11,6 +12,14 @@ namespace View_by_Distance.Distance.Models; public partial class E_Distance : IDistance { + internal record Mapped(string File, + double? Length, + string Link, + string Image); + + internal record Record(string File, + FaceRecognitionDotNet.FaceEncoding FaceRecognitionDotNetFaceEncoding); + private readonly List _Moved; private readonly List _Debug; private readonly List _Renamed; @@ -213,7 +222,7 @@ public partial class E_Distance : IDistance Face[] filteredFaces = (from l in faces where l.FaceEncoding is not null && l.Location is not null && l.OutputResolution is not null select l).ToArray(); if (filteredFaces.Length != faces.Count) checkFaces.Clear(); - foreach (LocationContainer? locationContainer in locationContainers) + foreach (LocationContainer locationContainer in locationContainers) { if (_Renamed.Contains(locationContainer.File)) continue; @@ -317,7 +326,7 @@ public partial class E_Distance : IDistance public static void SaveFaceDistances(Property.Models.Configuration configuration, SortingContainer[] sortingContainers) { - string eDistanceContentCollectionDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(E_Distance), "([])"); + string eDistanceContentCollectionDirectory = IResult.GetResultsDateGroupDirectory(configuration, nameof(E_Distance), "([])"); if (!Directory.Exists(eDistanceContentCollectionDirectory)) _ = Directory.CreateDirectory(eDistanceContentCollectionDirectory); #pragma warning disable @@ -480,7 +489,7 @@ public partial class E_Distance : IDistance return results; } - private static void ReviewLocationContainerDistanceTolerance(float locationContainerDistanceTolerance, DateTime dateTime, DateTime yesterday, List<(string File, FaceRecognitionDotNet.FaceEncoding FaceRecognitionDotNetFaceEncoding)> collection) + private static void ReviewLocationContainerDistanceTolerance(float locationContainerDistanceTolerance, DateTime dateTime, DateTime yesterday, List records) { FileInfo fileInfo; List files = new(); @@ -488,14 +497,14 @@ public partial class E_Distance : IDistance List faceDistanceLengths; List firstPassFailures = new(); List faceDistanceEncodings = new(); - faceDistanceEncoding = new(collection[0].FaceRecognitionDotNetFaceEncoding); - foreach ((string file, FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding) in collection) + faceDistanceEncoding = new(records[0].FaceRecognitionDotNetFaceEncoding); + foreach (Record record in records) { - files.Add(file); - faceDistanceEncodings.Add(new(faceRecognitionDotNetFaceEncoding)); + files.Add(record.File); + faceDistanceEncodings.Add(new(record.FaceRecognitionDotNetFaceEncoding)); } if (faceDistanceEncoding is null) - throw new Exception(); + throw new NullReferenceException(nameof(faceDistanceEncoding)); faceDistanceLengths = FaceRecognition.FaceDistances(new(faceDistanceEncodings), faceDistanceEncoding); if (faceDistanceLengths.Count != files.Count) throw new Exception(); @@ -505,14 +514,14 @@ public partial class E_Distance : IDistance continue; firstPassFailures.Add(files[i]); } - faceDistanceEncoding = new(collection[^1].FaceRecognitionDotNetFaceEncoding); - foreach ((string file, FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding) in collection) + faceDistanceEncoding = new(records[^1].FaceRecognitionDotNetFaceEncoding); + foreach (Record record in records) { - files.Add(file); - faceDistanceEncodings.Add(new(faceRecognitionDotNetFaceEncoding)); + files.Add(record.File); + faceDistanceEncodings.Add(new(record.FaceRecognitionDotNetFaceEncoding)); } if (faceDistanceEncoding is null) - throw new Exception(); + throw new NullReferenceException(nameof(faceDistanceEncoding)); faceDistanceLengths = FaceRecognition.FaceDistances(new(faceDistanceEncodings), faceDistanceEncoding); if (faceDistanceLengths.Count != files.Count) throw new Exception(); @@ -534,21 +543,21 @@ public partial class E_Distance : IDistance void IDistance.ReviewLocationContainerDistanceTolerance(float locationContainerDistanceTolerance, ReadOnlyCollection> locationContainers) { string? json; + List records = new(); int? lastDirectoryNumber = null; DateTime dateTime = DateTime.Now; DateTime yesterday = DateTime.Now.AddDays(-1); Shared.Models.FaceEncoding? modelsFaceEncoding; FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding; - List<(string, FaceRecognitionDotNet.FaceEncoding)> collection = new(); - foreach (LocationContainer? locationContainer in locationContainers) + foreach (LocationContainer locationContainer in locationContainers) { if (locationContainer.DirectoryNumber is null) continue; if (lastDirectoryNumber is not null && locationContainer.DirectoryNumber.Value != lastDirectoryNumber.Value) { - if (collection.Count > 2) - ReviewLocationContainerDistanceTolerance(locationContainerDistanceTolerance, dateTime, yesterday, collection); - collection.Clear(); + if (records.Count > 2) + ReviewLocationContainerDistanceTolerance(locationContainerDistanceTolerance, dateTime, yesterday, records); + records.Clear(); } json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.Directories); if (json is null) @@ -557,11 +566,176 @@ public partial class E_Distance : IDistance if (modelsFaceEncoding is null) throw new NotSupportedException(); faceRecognitionDotNetFaceEncoding = FaceRecognition.LoadFaceEncoding(modelsFaceEncoding.RawEncoding); - collection.Add((locationContainer.File, faceRecognitionDotNetFaceEncoding)); + records.Add(new(locationContainer.File, faceRecognitionDotNetFaceEncoding)); lastDirectoryNumber = locationContainer.DirectoryNumber.Value; } - if (collection.Count > 2) - ReviewLocationContainerDistanceTolerance(locationContainerDistanceTolerance, dateTime, yesterday, collection); + if (records.Count > 2) + ReviewLocationContainerDistanceTolerance(locationContainerDistanceTolerance, dateTime, yesterday, records); + } + + private static void WriteVsCodeFiles(string eDistanceContentDirectory, string? displayDirectoryName, string directory) + { + string json; + string vsCodeDirectory = Path.Combine(directory, ".vscode"); + if (!Directory.Exists(vsCodeDirectory)) + _ = Directory.CreateDirectory(vsCodeDirectory); + if (displayDirectoryName is not null) + File.WriteAllText(Path.Combine(directory, $"_ {displayDirectoryName}.txt"), string.Empty); + json = "{ \"[markdown]\": { \"editor.wordWrap\": \"off\" }, \"foam.links.hover.enable\": false, \"foam.graph.style\": { \"background\": \"#202020\", \"node\": { \"note\": \"#f2cb1d\", \"distance\": \"green\", \"image\": \"orange\", \"placeholder\": \"white\", } } }"; + _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(vsCodeDirectory, "settings.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + json = string.Concat("{ \"version\": \"2.0.0\", \"tasks\": [ { \"label\": \"MKLink\", \"type\": \"shell\", \"command\": \"New-Item\", \"args\": [ \"-ItemType\", \"Junction\", \"-Path\", \"'", directory.Replace('\\', '/'), "/()'\", \"-Target\", \"'", eDistanceContentDirectory.Replace('\\', '/'), "'\" ], \"problemMatcher\": [] } ] }"); + _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(vsCodeDirectory, "tasks.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + } + + private static void MoveFiles(Dictionary keyValuePairs, List linkedOnce, List linkedTwice, List linkedThrice) + { + string checkFile; + string checkDirectory; + string? yearDirectory; + string? personNameDirectory; + string? personNameDirectoryName; + string? personKeyFormattedDirectory; + foreach (string file in linkedOnce) + keyValuePairs[file] += 1; + foreach (string file in linkedTwice) + keyValuePairs[file] += 1; + foreach (string file in linkedThrice) + keyValuePairs[file] += 1; + foreach (KeyValuePair keyValuePair in keyValuePairs) + { + personNameDirectory = Path.GetDirectoryName(keyValuePair.Key); + yearDirectory = Path.GetDirectoryName(personNameDirectory); + personNameDirectoryName = Path.GetFileName(personNameDirectory); + personKeyFormattedDirectory = Path.GetDirectoryName(yearDirectory); + if (string.IsNullOrEmpty(personNameDirectory) || string.IsNullOrEmpty(yearDirectory) || string.IsNullOrEmpty(personKeyFormattedDirectory) || string.IsNullOrEmpty(personNameDirectoryName)) + continue; + checkDirectory = Path.Combine(personKeyFormattedDirectory, $"{keyValuePair.Value}{new string(Convert.ToChar(65 + keyValuePair.Value), 7)}"); + if (checkDirectory == yearDirectory) + continue; + checkDirectory = Path.Combine(checkDirectory, personNameDirectoryName); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + checkFile = Path.Combine(checkDirectory, Path.GetFileName(keyValuePair.Key)); + if (File.Exists(checkFile)) + continue; + File.Move(keyValuePair.Key, checkFile); + } + } + + private static void SaveMappedForOutputResolutions(string eDistanceContentDirectory, float distanceTolerance, string personKeyFormatted, string? displayDirectoryName, List records, string directory) + { + string text; + string fileName; + FileInfo fileInfo; + List files = new(); + List lines = new(); + List linkedOnce = new(); + string fileNameWithoutExtension; + List linkedTwice = new(); + List linkedThrice = new(); + FaceDistance? faceDistanceEncoding; + List relativePaths = new(); + List faceDistanceLengths; + Uri uri = new(eDistanceContentDirectory); + Dictionary keyValuePairs = new(); + List faceDistanceEncodings = new(); + List results = new(); + foreach (Record record in records) + { + files.Add(record.File); + relativePaths.Add(uri.MakeRelativeUri(new(record.File)).OriginalString); + faceDistanceEncodings.Add(new(record.FaceRecognitionDotNetFaceEncoding)); + } + foreach (Record record in records) + { + lines.Clear(); + results.Clear(); + fileInfo = new(record.File); + if (files.Count > 1) + { + faceDistanceEncoding = new(record.FaceRecognitionDotNetFaceEncoding); + if (faceDistanceEncoding is null) + throw new NullReferenceException(nameof(faceDistanceEncoding)); + faceDistanceLengths = FaceRecognition.FaceDistances(new(faceDistanceEncodings), faceDistanceEncoding); + for (int i = 0; i < faceDistanceLengths.Count; i++) + { + fileName = Path.GetFileName(relativePaths[i]); + if (fileName == fileInfo.Name) + continue; + FaceDistance faceDistance = faceDistanceLengths[i]; + if (faceDistance.Length > distanceTolerance) + continue; + fileNameWithoutExtension = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(fileName)); + results.Add(new(files[i], faceDistance.Length, $"[[{fileNameWithoutExtension}]]", $"![{faceDistance.Length}]({relativePaths[i]})")); + } + } + results = (from l in results orderby l.Length select l).Take(3).ToList(); + keyValuePairs.Add(record.File, results.Count); + foreach (Mapped mapped in results) + { + if (!linkedOnce.Contains(mapped.File)) + { + linkedOnce.Add(mapped.File); + continue; + } + if (!linkedTwice.Contains(mapped.File)) + { + linkedTwice.Add(mapped.File); + continue; + } + if (!linkedThrice.Contains(mapped.File)) + { + linkedThrice.Add(mapped.File); + continue; + } + } + fileNameWithoutExtension = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(fileInfo.Name)); + lines.Add("---"); + lines.Add("type: \"distance\""); + lines.Add("---"); + lines.Add(string.Empty); + if (displayDirectoryName is null) + lines.Add($"## {personKeyFormatted}"); + else + lines.Add($"## {displayDirectoryName}"); + lines.Add(string.Empty); + lines.Add($"![0]({uri.MakeRelativeUri(new(record.File)).OriginalString})"); + lines.Add($"__{fileNameWithoutExtension}__"); + lines.Add(string.Empty); + lines.AddRange(results.Select(l => $"{l.Image}{Environment.NewLine}{l.Link}{Environment.NewLine}")); + text = string.Join(Environment.NewLine, lines); + if (!Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(directory, $"{fileNameWithoutExtension}.md"), text, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null)) + continue; + } + MoveFiles(keyValuePairs, linkedOnce, linkedTwice, linkedThrice); + } + + void IDistance.SaveMappedForOutputResolutions(string a2PeopleContentDirectory, string eDistanceContentDirectory, float[] rangeDistanceTolerance, ReadOnlyCollection> locationContainers, string personKeyFormatted, string? displayDirectoryName) + { + string? json; + List records = new(); + Shared.Models.FaceEncoding? modelsFaceEncoding; + FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding; + foreach (LocationContainer locationContainer in locationContainers) + { + json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.Directories); + if (json is null) + continue; + modelsFaceEncoding = JsonSerializer.Deserialize(json); + if (modelsFaceEncoding is null) + throw new NotSupportedException(); + faceRecognitionDotNetFaceEncoding = FaceRecognition.LoadFaceEncoding(modelsFaceEncoding.RawEncoding); + records.Add(new(locationContainer.File, faceRecognitionDotNetFaceEncoding)); + } + string directory = Path.Combine(a2PeopleContentDirectory, rangeDistanceTolerance[1].ToString(), personKeyFormatted); + if (!Directory.Exists(directory)) + _ = Directory.CreateDirectory(directory); + if (records.Count > 0) + { + WriteVsCodeFiles(eDistanceContentDirectory, displayDirectoryName, directory); + SaveMappedForOutputResolutions(eDistanceContentDirectory, rangeDistanceTolerance[1], personKeyFormatted, displayDirectoryName, records, directory); + } + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(directory); } } \ No newline at end of file diff --git a/Face/Models/_D_Face.cs b/Face/Models/_D_Face.cs index f5635da..95f5b1c 100644 --- a/Face/Models/_D_Face.cs +++ b/Face/Models/_D_Face.cs @@ -307,7 +307,7 @@ public class D_Face if (location is null) continue; if (!results.Any(l => l.WholePercentages == locationContainer.WholePercentages)) - results.Add(new(locationContainer.FromDistanceContent, locationContainer.DirectoryNumber, locationContainer.File, locationContainer.PersonKey, locationContainer.Id, locationContainer.WholePercentages, locationContainer.Directories, rectangle.Value, location)); + results.Add(new(locationContainer.FromDistanceContent, locationContainer.DirectoryNumber, locationContainer.File, locationContainer.PersonKey, locationContainer.DisplayDirectoryName, locationContainer.Id, locationContainer.WholePercentages, locationContainer.Directories, rectangle.Value, location)); } } if (results.Count > 0) diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index 9f3cc19..35d3a3e 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -260,7 +260,7 @@ public partial class DlibDotNet if (runToDoCollectionFirst) mapLogic = null; else - mapLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleSingletonDirectory, eDistanceContentDirectory); + mapLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); foreach (string outputResolution in _Configuration.OutputResolutions) { if (outputResolution.Any(l => char.IsNumber(l))) @@ -323,7 +323,7 @@ public partial class DlibDotNet } fileNameToCollection = !Directory.Exists(fPhotoPrismSingletonDirectory) ? fileNameToCollection = new() : F_PhotoPrism.GetFileNameToCollection(fPhotoPrismSingletonDirectory); B_Metadata metadata = new(_Configuration.PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, bResultsFullGroupDirectory); - mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleSingletonDirectory, eDistanceContentDirectory); + mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); FullDoWork(argZero, propertyRoot, ticks, aResultsFullGroupDirectory, bResultsFullGroupDirectory, t, containers, propertyLogic, metadata, fileNameToCollection, mapLogic); ReadOnlyCollection distinctFilteredItems = Shared.Models.Stateless.Methods.IContainer.GetItems(_Configuration.PropertyConfiguration, new(containers), distinctItems: true, filterItems: true); if (_Configuration.LookForAbandoned) @@ -848,6 +848,7 @@ public partial class DlibDotNet configuration.PersonCharacters.ToArray(), configuration.RangeDaysDeltaTolerance, configuration.RangeDistanceTolerance, + configuration.SaveMappedRelations, configuration.SaveSortingWithoutPerson, configuration.SkipNotSkipDirectories, configuration.SortingMaximumPerKey, diff --git a/Instance/Models/Binder/Configuration.cs b/Instance/Models/Binder/Configuration.cs index 24a9b78..7e9fed7 100644 --- a/Instance/Models/Binder/Configuration.cs +++ b/Instance/Models/Binder/Configuration.cs @@ -75,6 +75,8 @@ public class Configuration public bool? SaveFullYearOfRandomFiles { get; set; } public bool? SaveIndividually { get; set; } public string[]? SaveMappedForOutputResolutions { get; set; } + public bool? SaveMappedRelations { get; set; } + public string[]? SaveMappedRelationsForOutputResolutions { get; set; } public string[]? SavePropertyShortcutsForOutputResolutions { get; set; } public string[]? SaveRandomForOutputResolutions { get; set; } public bool? SaveResizedSubfiles { get; set; } @@ -171,6 +173,7 @@ public class Configuration if (configuration?.SaveFullYearOfRandomFiles is null) throw new NullReferenceException(nameof(configuration.SaveFullYearOfRandomFiles)); if (configuration?.SaveIndividually is null) throw new NullReferenceException(nameof(configuration.SaveIndividually)); // if (configuration?.SaveMappedForOutputResolutions is null) throw new NullReferenceException(nameof(configuration.SaveMappedForOutputResolutions)); + if (configuration?.SaveMappedRelations is null) throw new NullReferenceException(nameof(configuration.SaveMappedRelations)); // if (configuration?.SavePropertyShortcutsForOutputResolutions is null) throw new NullReferenceException(nameof(configuration.SavePropertyShortcutsForOutputResolutions)); // if (configuration?.SaveRandomForOutputResolutions is null) throw new NullReferenceException(nameof(configuration.SaveRandomForOutputResolutions)); if (configuration?.SaveResizedSubfiles is null) throw new NullReferenceException(nameof(configuration.SaveResizedSubfiles)); @@ -251,9 +254,10 @@ public class Configuration configuration.SaveFaceDistancesForOutputResolutions ?? Array.Empty(), configuration.SaveFaceLandmarkForOutputResolutions ?? Array.Empty(), configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions ?? Array.Empty(), - configuration.SaveIndividually.Value, configuration.SaveFullYearOfRandomFiles.Value, + configuration.SaveIndividually.Value, configuration.SaveMappedForOutputResolutions ?? Array.Empty(), + configuration.SaveMappedRelations.Value, configuration.SavePropertyShortcutsForOutputResolutions ?? Array.Empty(), configuration.SaveRandomForOutputResolutions ?? Array.Empty(), configuration.SaveResizedSubfiles.Value, diff --git a/Instance/Models/Configuration.cs b/Instance/Models/Configuration.cs index 7313785..ebc78fe 100644 --- a/Instance/Models/Configuration.cs +++ b/Instance/Models/Configuration.cs @@ -64,9 +64,10 @@ public record Configuration(Property.Models.Configuration PropertyConfiguration, string[] SaveFaceDistancesForOutputResolutions, string[] SaveFaceLandmarkForOutputResolutions, string[] SaveFilteredOriginalImagesFromJLinksForOutputResolutions, - bool SaveIndividually, bool SaveFullYearOfRandomFiles, + bool SaveIndividually, string[] SaveMappedForOutputResolutions, + bool SaveMappedRelations, string[] SavePropertyShortcutsForOutputResolutions, string[] SaveRandomForOutputResolutions, bool SaveResizedSubfiles, diff --git a/Map/Models/Configuration.cs b/Map/Models/Configuration.cs index d6178b3..457e08d 100644 --- a/Map/Models/Configuration.cs +++ b/Map/Models/Configuration.cs @@ -1,80 +1,23 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - namespace View_by_Distance.Map.Models; -public class Configuration -{ - - public bool DeletePossibleDuplicates { get; internal set; } - public bool DistanceMoveUnableToMatch { init; get; } - public bool DistanceRenameToMatch { init; get; } - public int FaceConfidencePercent { init; get; } - public int FaceDistancePermyriad { init; get; } - public string FacePartsFileNameExtension { init; get; } - public string FacesFileNameExtension { init; get; } - public string FacesHiddenFileNameExtension { init; get; } - public float? LocationContainerDistanceTolerance { init; get; } - public int LocationDigits { init; get; } - public string MappingDefaultName { init; get; } - public int PersonBirthdayFirstYear { init; get; } - public string PersonBirthdayFormat { init; get; } - public char[] PersonCharacters { init; get; } - public int RangeDaysDeltaTolerance { init; get; } - public double RangeDistanceTolerance { init; get; } - public bool SaveSortingWithoutPerson { init; get; } - public string[] SkipNotSkipDirectories { init; get; } - public int SortingMaximumPerKey { init; get; } - public int SortingMinimumToUseSigma { init; get; } - - [JsonConstructor] - public Configuration(bool deletePossibleDuplicates, - bool distanceMoveUnableToMatch, - bool distanceRenameToMatch, - int faceConfidencePercent, - int faceDistancePermyriad, - float? locationContainerDistanceTolerance, - int locationDigits, - string mappingDefaultName, - int personBirthdayFirstYear, - string personBirthdayFormat, - char[] personCharacters, - int[] rangeDaysDeltaTolerance, - float[] rangeDistanceTolerance, - bool saveSortingWithoutPerson, - string[] skipNotSkipDirectories, - int sortingMaximumPerKey, - int sortingMinimumToUseSigma, - string facesFileNameExtension, - string facesHiddenFileNameExtension, - string facePartsFileNameExtension) - { - LocationDigits = locationDigits; - PersonCharacters = personCharacters; - MappingDefaultName = mappingDefaultName; - PersonBirthdayFormat = personBirthdayFormat; - SortingMaximumPerKey = sortingMaximumPerKey; - DistanceRenameToMatch = distanceRenameToMatch; - FaceConfidencePercent = faceConfidencePercent; - FaceDistancePermyriad = faceDistancePermyriad; - FacesFileNameExtension = facesFileNameExtension; - SkipNotSkipDirectories = skipNotSkipDirectories; - PersonBirthdayFirstYear = personBirthdayFirstYear; - RangeDistanceTolerance = rangeDistanceTolerance[1]; - DeletePossibleDuplicates = deletePossibleDuplicates; - SaveSortingWithoutPerson = saveSortingWithoutPerson; - SortingMinimumToUseSigma = sortingMinimumToUseSigma; - RangeDaysDeltaTolerance = rangeDaysDeltaTolerance[1]; - DistanceMoveUnableToMatch = distanceMoveUnableToMatch; - FacePartsFileNameExtension = facePartsFileNameExtension; - FacesHiddenFileNameExtension = facesHiddenFileNameExtension; - LocationContainerDistanceTolerance = locationContainerDistanceTolerance; - } - - public override string ToString() - { - string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); - return result; - } - -} \ No newline at end of file +public record Configuration(bool DeletePossibleDuplicates, + bool DistanceMoveUnableToMatch, + bool DistanceRenameToMatch, + int FaceConfidencePercent, + int FaceDistancePermyriad, + float? LocationContainerDistanceTolerance, + int LocationDigits, + string MappingDefaultName, + int PersonBirthdayFirstYear, + string PersonBirthdayFormat, + char[] PersonCharacters, + int[] RangeDaysDeltaTolerance, + float[] RangeDistanceTolerance, + bool SaveMappedRelations, + bool SaveSortingWithoutPerson, + string[] SkipNotSkipDirectories, + int SortingMaximumPerKey, + int SortingMinimumToUseSigma, + string FacesFileNameExtension, + string FacesHiddenFileNameExtension, + string FacePartsFileNameExtension); \ No newline at end of file diff --git a/Map/Models/MapLogic.cs b/Map/Models/MapLogic.cs index 4f74659..1e74062 100644 --- a/Map/Models/MapLogic.cs +++ b/Map/Models/MapLogic.cs @@ -1,4 +1,4 @@ -using Humanizer; +using Humanizer; using ShellProgressBar; using System.Collections.ObjectModel; using System.Globalization; @@ -226,7 +226,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic private readonly ReadOnlyDictionary>> _IdToLocationContainers; private readonly ReadOnlyDictionary>> _IdThenWholePercentagesToPersonContainers; - public MapLogic(int maxDegreeOfParallelism, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration? configuration, Shared.Models.Methods.IDistance distance, ReadOnlyCollection personContainers, long ticks, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) + public MapLogic(int maxDegreeOfParallelism, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration? configuration, Shared.Models.Methods.IDistance distance, ReadOnlyCollection personContainers, long ticks, string? a2PeopleContentDirectory, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) { _Ticks = ticks; _Configuration = configuration; @@ -240,7 +240,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic Dictionary> skipNotSkipCollection = new(); List> locationContainers = new(); string? rootDirectoryParent = Path.GetDirectoryName(propertyConfiguration.RootDirectory); - string eDistanceContentTicksDirectory = Path.Combine(eDistanceContentDirectory, $"({ticks})"); + string eDistanceContentTicksDirectory = Path.Combine(eDistanceContentDirectory, ticks.ToString()); ReadOnlyDictionary>> idThenWholePercentagesToPersonContainers; if (string.IsNullOrEmpty(rootDirectoryParent)) throw new NullReferenceException(nameof(rootDirectoryParent)); @@ -270,13 +270,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic } List records = Stateless.MapLogic.DeleteEmptyDirectoriesAndGetCollection(configuration, ticks, eDistanceContentDirectory, readOnlyPersonKeyFormattedToNewestPersonKeyFormatted, readOnlyPersonKeyFormattedCollection); ReadOnlyCollection<(Stateless.MapLogic.PersonKeyFormattedIdThenWholePercentages, PersonContainer)> readOnlyPossiblyNewPersonDisplayDirectoryNamesAndPersonContainer; - locationContainers.AddRange(Stateless.MapLogic.GetLocationContainers(distance, maxDegreeOfParallelism, configuration, ticks, personContainers, eDistanceContentDirectory, skipCollection, records)); - int lossCount = records.Count - locationContainers.Count; ReadOnlyCollection personKeyFormattedIdThenWholePercentagesCollection = Stateless.MapLogic.GetPersonKeyFormattedIdThenWholePercentages(configuration, ticks, records); - int unableToMatchCount = records.Count - personKeyFormattedIdThenWholePercentagesCollection.Count; - if (lossCount != 0 || unableToMatchCount != 0) - if (lossCount != 0 || unableToMatchCount != 0) - { } // { Dictionary personKeyToCount = new(); @@ -314,6 +308,20 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic ticks, personContainers, readOnlyPersonKeyToCount)); + locationContainers.AddRange(Stateless.MapLogic.GetLocationContainers(distance, + maxDegreeOfParallelism, + configuration, + ticks, + personContainers, + skipCollection, + records)); + int lossCount = records.Count - locationContainers.Count; + int unableToMatchCount = records.Count - personKeyFormattedIdThenWholePercentagesCollection.Count; + if (lossCount != 0 || unableToMatchCount != 0) + if (lossCount != 0 || unableToMatchCount != 0) + { } + if (!string.IsNullOrEmpty(a2PeopleContentDirectory) && configuration.SaveMappedRelations) + Stateless.MapLogic.SaveMappedRelations(configuration, distance, a2PeopleContentDirectory, eDistanceContentDirectory, ticks, locationContainers, readOnlyPersonKeyFormattedToPersonContainer, readOnlyPersonKeyToPersonContainerCollection); } _PersonKeyToCount = readOnlyPersonKeyToCount; _EDistanceContentTicksDirectory = eDistanceContentTicksDirectory; @@ -548,11 +556,11 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic (ticks, directory) = GetDirectory(_Configuration, saveIndividually, padLeft, mapping.SegmentC, by, mapping.MappingFromItem); if (ticks is null || string.IsNullOrEmpty(directory)) continue; - personDirectory = Path.Combine(directory, $"X]{ticks}"); + personDirectory = Path.Combine(directory, $"X+{ticks}"); if (saveIndividually) { directory = Path.Combine(directory, mapping.MappingFromItem.Id.ToString()); - results.Add(new(Path.Combine(directory, $"X]{ticks}"))); + results.Add(new(Path.Combine(directory, $"X+{ticks}"))); } distinct.Add(mapping.MappingFromItem.Id); distinct.Add(mapping.SortingContainer.Sorting.Id); @@ -686,6 +694,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic FaceDistance faceDistanceLength; List? wholePercentagesCollection; bool skipNotSkipCollectionAny = _SkipNotSkipCollection.Count > 0; + float distanceTolerance = _Configuration.RangeDistanceTolerance[1]; for (int j = 0; j < faceDistanceLengths.Count; j++) { if (faceDistanceEncoding.WholePercentages is null) @@ -705,7 +714,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic throw new NotSupportedException(); if (faceDistanceLength.Length == 0) continue; - sorting = ISorting.Get(_Configuration.FaceDistancePermyriad, _Configuration.RangeDistanceTolerance, faceDistanceEncoding, faceDistanceLength); + sorting = ISorting.Get(_Configuration.FaceDistancePermyriad, distanceTolerance, faceDistanceEncoding, faceDistanceLength); if (sorting.DistancePermyriad == 0) continue; if (sorting.Id == faceDistanceEncoding.Id) diff --git a/Map/Models/Stateless/MapLogic.cs b/Map/Models/Stateless/MapLogic.cs index 2d6e4e6..7ee6ba9 100644 --- a/Map/Models/Stateless/MapLogic.cs +++ b/Map/Models/Stateless/MapLogic.cs @@ -1,4 +1,4 @@ -using Humanizer; +using Humanizer; using ShellProgressBar; using System.Collections.ObjectModel; using System.Diagnostics; @@ -20,6 +20,12 @@ internal abstract class MapLogic bool IsDefault, string MappedFaceFile); + internal record MappedFile(long PersonKey, + string PersonKeyFormatted, + string PersonDisplayDirectoryName, + int? DirectoryNumber, + string File); + internal record Duplicate(long PersonKey, int Id, string File, @@ -227,10 +233,11 @@ internal abstract class MapLogic } } - private static List<(long, int?, string)> GetDisplayDirectoryAllFiles(string fileNameExtension, ReadOnlyCollection personContainers) + private static List GetDisplayDirectoryAllFiles(string fileNameExtension, string personBirthdayFormat, ReadOnlyCollection personContainers) { - List<(long, int?, string)> results = new(); + List results = new(); string fileName; + string personKeyFormatted; List distinct = new(); foreach (PersonContainer personContainer in personContainers) { @@ -244,7 +251,8 @@ internal abstract class MapLogic if (distinct.Contains(fileName)) continue; distinct.Add(fileName); - results.Add(new(personContainer.Key.Value, null, personContainer.DisplayDirectoryAllFiles[i])); + personKeyFormatted = IPersonBirthday.GetFormatted(personBirthdayFormat, personContainer.Key.Value); + results.Add(new(personContainer.Key.Value, personKeyFormatted, personContainer.DisplayDirectoryName, null, personContainer.DisplayDirectoryAllFiles[i])); } } return results; @@ -280,11 +288,14 @@ internal abstract class MapLogic { List results = new(); float? totalDays; + long? next = null; + string? checkDirectory; string ticksDirectoryName; DateTime directoryDateTime; DirectoryInfo directoryInfo; long? lastDirectoryTicks = null; DateTime dateTime = DateTime.Now; + long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks; for (int i = 1; i < 5; i++) _ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory); if (!Directory.Exists(eDistanceContentDirectory)) @@ -293,12 +304,20 @@ internal abstract class MapLogic foreach (string ticksDirectory in ticksDirectories) { ticksDirectoryName = Path.GetFileName(ticksDirectory); - if (ticksDirectoryName.Length < 3 || ticksDirectoryName.First() != '(' || ticksDirectoryName[^1] != ')') - continue; - if (!long.TryParse(ticksDirectoryName[1..^1], out long directoryTicks)) + if (!long.TryParse(ticksDirectoryName, out long directoryTicks)) + throw new NotSupportedException(); + if (next is null) + next = new DateTime(directoryTicks).Ticks; + else { - if (!long.TryParse(ticksDirectoryName[1..^4], out directoryTicks)) - throw new NotSupportedException(); + next += month; + checkDirectory = Path.GetDirectoryName(ticksDirectory); + if (string.IsNullOrEmpty(checkDirectory)) + { + if (!string.IsNullOrEmpty(checkDirectory)) + Directory.Move(ticksDirectory, Path.Combine(checkDirectory, next.Value.ToString())); + continue; + } } directoryInfo = new(ticksDirectory); directoryDateTime = new DateTime(directoryTicks); @@ -568,15 +587,15 @@ internal abstract class MapLogic return result; } - private static List<(long PersonKey, int? DirectoryNumber, string File)> GetCollection(Configuration configuration, ReadOnlyCollection personContainers, List records) + private static List GetMappedFiles(Configuration configuration, ReadOnlyCollection personContainers, List records) { - List<(long PersonKey, int? DirectoryNumber, string File)> results = new(); + List results = new(); string file; long personKey; string fileName; List distinct = new(); PersonBirthday? personBirthday; - results.AddRange(GetDisplayDirectoryAllFiles(configuration.FacesFileNameExtension, personContainers)); + results.AddRange(GetDisplayDirectoryAllFiles(configuration.FacesFileNameExtension, configuration.PersonBirthdayFormat, personContainers)); foreach (Record record in records) { personBirthday = IPersonBirthday.GetPersonBirthday(configuration.PersonBirthdayFormat, record.PersonKeyFormatted); @@ -587,7 +606,7 @@ internal abstract class MapLogic continue; distinct.Add(fileName); personKey = personBirthday.Value.Ticks; - results.Add(new(personKey, record.DirectoryNumber, record.MappedFaceFile)); + results.Add(new(personKey, record.PersonKeyFormatted, record.PersonDisplayDirectoryName, record.DirectoryNumber, record.MappedFaceFile)); } for (int i = results.Count - 1; i > -1; i--) { @@ -602,24 +621,24 @@ internal abstract class MapLogic if (!File.Exists(file)) continue; File.Move(file, file[..^4]); - results[i] = new(results[i].PersonKey, results[i].DirectoryNumber, file[..^4]); + results[i] = new(results[i].PersonKey, results[i].PersonKeyFormatted, results[i].PersonDisplayDirectoryName, results[i].DirectoryNumber, file[..^4]); } return results; } - private static void ParallelFor(Configuration configuration, string eDistanceContentDirectory, Dictionary> skipCollection, List> locationContainers, long personKey, int? directoryNumber, string file) + private static void ParallelFor(Configuration configuration, Dictionary> skipCollection, List> locationContainers, MappedFile mappedFile) { string checkFile; string[] fileMatches; const string lnk = ".lnk"; int? id, wholePercentages; + const bool fromDistanceContent = true; IReadOnlyList directories; List<(string File, int WholePercentages)>? wholePercentagesCollection; - bool fromDistanceContent = !file.EndsWith(lnk) && file.Contains(eDistanceContentDirectory); - if (!file.EndsWith(lnk)) - (id, wholePercentages) = IMapping.GetConverted(configuration.FacesFileNameExtension, file); + if (!mappedFile.File.EndsWith(lnk)) + (id, wholePercentages) = IMapping.GetConverted(configuration.FacesFileNameExtension, mappedFile.File); else - (id, wholePercentages) = IMapping.GetConverted(configuration.FacesFileNameExtension, file[..^4]); + (id, wholePercentages) = IMapping.GetConverted(configuration.FacesFileNameExtension, mappedFile.File[..^4]); if (id is null || wholePercentages is null) return; if (skipCollection.TryGetValue(id.Value, out wholePercentagesCollection)) @@ -636,13 +655,13 @@ internal abstract class MapLogic continue; } } - if (file.EndsWith(lnk) || (!configuration.DistanceMoveUnableToMatch && !configuration.DistanceRenameToMatch) || !File.Exists(file)) + if (mappedFile.File.EndsWith(lnk) || (!configuration.DistanceMoveUnableToMatch && !configuration.DistanceRenameToMatch) || !File.Exists(mappedFile.File)) directories = new List(); else - directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(file); + directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(mappedFile.File); RectangleF? rectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, wholePercentages.Value); lock (locationContainers) - locationContainers.Add(new(fromDistanceContent, directoryNumber, file, personKey, id.Value, wholePercentages.Value, directories, rectangle, null)); + locationContainers.Add(new(fromDistanceContent, mappedFile.DirectoryNumber, mappedFile.File, mappedFile.PersonKey, mappedFile.PersonDisplayDirectoryName, id.Value, wholePercentages.Value, directories, rectangle, null)); } private static void LookForPossibleDuplicates(Configuration configuration, ReadOnlyCollection> locationContainers) @@ -852,6 +871,7 @@ internal abstract class MapLogic int? wholePercentages; string? checkDirectory; ProgressBar progressBar; + string? yearDirectoryName; string[] yearDirectories; string personKeyFormatted; string? personFirstInitial; @@ -896,7 +916,7 @@ internal abstract class MapLogic { timeSpan = new TimeSpan(DateTime.Now.Ticks - ticksDirectory.DirectoryDateTime.Ticks); if (timeSpan.TotalDays > 6) - throw new Exception($"{configuration.MappingDefaultName} are only allowed within x days!"); + throw new Exception($"{configuration.MappingDefaultName} <{ticksDirectory.DirectoryDateTime}> are only allowed within x days!"); } yearDirectories = Directory.GetDirectories(personKeyFormattedDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string yearDirectory in yearDirectories) @@ -907,6 +927,22 @@ internal abstract class MapLogic personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string file in files) File.Delete(file); + yearDirectoryName = Path.GetFileName(yearDirectory); + if (!string.IsNullOrEmpty(yearDirectoryName) && yearDirectoryName.Length != 8) + { + checkDirectory = Path.Combine(personKeyFormattedDirectory, "abcdefgh"); + if (Directory.Exists(checkDirectory)) + { + MoveDirectory(personNameDirectories, checkDirectory); + if (!check) + check = true; + continue; + } + Directory.Move(yearDirectory, checkDirectory); + if (!check) + check = true; + continue; + } foreach (string personNameDirectory in personNameDirectories) { directoryNumber++; @@ -916,7 +952,7 @@ internal abstract class MapLogic { if (personKeyFormatted.Length != configuration.PersonBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormatted, configuration.PersonBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) continue; - checkDirectory = Path.Combine(yearDirectory, $"X]{dateTime.Ticks}"); + checkDirectory = Path.Combine(yearDirectory, $"X+{dateTime.Ticks}"); if (Directory.Exists(checkDirectory)) { Directory.Delete(yearDirectory, recursive: true); @@ -932,7 +968,7 @@ internal abstract class MapLogic checkDirectory = Path.GetDirectoryName(ticksDirectory.Directory); if (checkDirectory is null) continue; - checkDirectory = Path.Combine(checkDirectory, $"({ticksDirectory.AlternateDirectoryDateTime.Ticks})"); + checkDirectory = Path.Combine(checkDirectory, ticksDirectory.AlternateDirectoryDateTime.Ticks.ToString()); if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); checkDirectory = Path.Combine(checkDirectory, personKeyFormatted); @@ -1034,6 +1070,33 @@ internal abstract class MapLogic return results; } + private static void MoveDirectory(string[] personNameDirectories, string destination) + { + string[] files; + string checkFile; + string checkDirectory; + string? personNameDirectoryName; + foreach (string personNameDirectory in personNameDirectories) + { + personNameDirectoryName = Path.GetFileName(personNameDirectory); + checkDirectory = Path.Combine(destination, personNameDirectoryName); + if (personNameDirectory == checkDirectory) + continue; + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + files = Directory.GetFiles(personNameDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string file in files) + { + checkFile = Path.Combine(checkDirectory, Path.GetFileName(file)); + if (checkFile == file) + continue; + if (File.Exists(checkFile)) + continue; + File.Move(file, checkFile); + } + } + } + internal static void SetKeyValuePairsAndAddToCollections(Configuration configuration, ReadOnlyCollection personContainers, Dictionary personKeyToPersonContainer, ReadOnlyCollection personKeyFormattedIdThenWholePercentagesCollection, Dictionary personKeyToCount, Dictionary personKeyFormattedToPersonContainer, Dictionary> personKeyToPersonContainerCollection, List<(PersonKeyFormattedIdThenWholePercentages, PersonContainer)> possiblyNewPersonDisplayDirectoryNamesAndPersonContainer) { PersonBirthday? personBirthday; @@ -1228,21 +1291,21 @@ internal abstract class MapLogic } } - internal static List> GetLocationContainers(Shared.Models.Methods.IDistance distance, int maxDegreeOfParallelism, Configuration configuration, long ticks, ReadOnlyCollection personContainers, string eDistanceContentDirectory, Dictionary> skipCollection, List records) + internal static List> GetLocationContainers(Shared.Models.Methods.IDistance distance, int maxDegreeOfParallelism, Configuration configuration, long ticks, ReadOnlyCollection personContainers, Dictionary> skipCollection, List records) { List> results = new(); - List<(long PersonKey, int? DirectoryNumber, string File)> collection = GetCollection(configuration, personContainers, records); - if (collection.Count > 0 && (configuration.DistanceMoveUnableToMatch || configuration.DistanceRenameToMatch)) + List mappedFiles = GetMappedFiles(configuration, personContainers, records); + if (mappedFiles.Count > 0 && (configuration.DistanceMoveUnableToMatch || configuration.DistanceRenameToMatch)) { int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)"; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - using ProgressBar progressBar = new(collection.Count, message, options); - _ = Parallel.For(0, collection.Count, parallelOptions, (i, state) => + using ProgressBar progressBar = new(mappedFiles.Count, message, options); + _ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) => { progressBar.Tick(); - ParallelFor(configuration, eDistanceContentDirectory, skipCollection, results, collection[i].PersonKey, collection[i].DirectoryNumber, collection[i].File); + ParallelFor(configuration, skipCollection, results, mappedFiles[i]); }); } ReadOnlyCollection> locationContainers = new(results.OrderBy(l => l.DirectoryNumber).ToArray()); @@ -1702,4 +1765,64 @@ internal abstract class MapLogic } } + private static ReadOnlyCollection>> GetCollections(List> locationContainers) + { + List>> results = new(); + List>? collection; + Dictionary>> keyValuePairs = new(); + foreach (LocationContainer locationContainer in locationContainers) + { + if (!locationContainer.FromDistanceContent) + continue; + if (!keyValuePairs.TryGetValue(locationContainer.PersonKey, out collection)) + { + keyValuePairs.Add(locationContainer.PersonKey, new()); + if (!keyValuePairs.TryGetValue(locationContainer.PersonKey, out collection)) + throw new Exception(); + } + collection.Add(locationContainer); + } + foreach (KeyValuePair>> keyValuePair in keyValuePairs) + results.Add(new(keyValuePair.Value)); + return new(results); + } + + private static string? GetDisplayDirectoryName(ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection, ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer, long personKey, string personKeyFormatted) + { + string? result; + PersonContainer? personContainer; + List? collection; + _ = readOnlyPersonKeyToPersonContainerCollection.TryGetValue(personKey, out collection); + if (collection is not null) + result = collection[0].DisplayDirectoryName; + else + { + if (!readOnlyPersonKeyFormattedToPersonContainer.TryGetValue(personKeyFormatted, out personContainer)) + result = null; + else + result = personContainer.DisplayDirectoryName; + } + return result; + } + + internal static void SaveMappedRelations(Configuration configuration, Shared.Models.Methods.IDistance distance, string a2PeopleContentDirectory, string eDistanceContentDirectory, long ticks, List> locationContainers, ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer, ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection) + { + string personKeyFormatted; + string? displayDirectoryName; + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + string message = $") Save Mapped Relations - {totalSeconds} total second(s)"; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + ReadOnlyCollection>> collections = GetCollections(locationContainers); + using ProgressBar progressBar = new(collections.Count, message, options); + foreach (ReadOnlyCollection> collection in collections) + { + progressBar.Tick(); + if (collection.Count == 0) + continue; + personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, collection[0].PersonKey); + displayDirectoryName = GetDisplayDirectoryName(readOnlyPersonKeyToPersonContainerCollection, readOnlyPersonKeyFormattedToPersonContainer, collection[0].PersonKey, personKeyFormatted); + distance.SaveMappedForOutputResolutions(a2PeopleContentDirectory, eDistanceContentDirectory, configuration.RangeDistanceTolerance, collection, personKeyFormatted, displayDirectoryName); + } + } + } \ No newline at end of file diff --git a/Shared/.kanbn/tasks/google-api-for-family.md b/Shared/.kanbn/tasks/google-api-for-family.md new file mode 100644 index 0000000..d4f6b2f --- /dev/null +++ b/Shared/.kanbn/tasks/google-api-for-family.md @@ -0,0 +1,10 @@ +--- +created: 2023-08-19T18:52:14.422Z +updated: 2023-08-19T18:52:14.418Z +assigned: "" +progress: 0 +tags: [] +started: 2023-08-19T18:52:14.422Z +--- + +# Google API for family diff --git a/Shared/.vscode/tasks.json b/Shared/.vscode/tasks.json index 9a197a0..c4433af 100644 --- a/Shared/.vscode/tasks.json +++ b/Shared/.vscode/tasks.json @@ -19,12 +19,6 @@ "command": "& L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net7.0/win-x64/publish/File-Folder-Helper.exe s M .kanbn/tasks", "problemMatcher": [] }, - { - "label": "File-Folder-Helper AOT s G 638263275367065735", - "type": "shell", - "command": "& L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net7.0/win-x64/publish/File-Folder-Helper.exe s G 'D:/1-Images-A/Images-dd514b88-Results/A2) People/dd514b88/{}' -g 'D:/1-Images-A/Images-dd514b88-Results/A2) People/dd514b88/([])/File-Folder-Helper/638263275367065735/638263275367065735.ged' -d 'D:/1-Images-A/Images-dd514b88-Results/A2) People/dd514b88/{2}'", - "problemMatcher": [] - }, { "label": "File-Folder-Helper AOT s C Backup", "type": "shell", @@ -34,7 +28,13 @@ { "label": "File-Folder-Helper AOT s G File System to Genealogical Data Communication", "type": "shell", - "command": "& L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net7.0/win-x64/publish/File-Folder-Helper.exe s G 'D:/1-Images-A/Images-dd514b88-Results/A2) People/dd514b88/([])/File-Folder-Helper/638268289384407819' -d 'D:/1-Images-A/Images-dd514b88-Results/A2) People/dd514b88/{2}'", + "command": "& L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net7.0/win-x64/publish/File-Folder-Helper.exe s G 'D:/1-Images-A/Images-dd514b88-Results/A2)People/dd514b88/([])/File-Folder-Helper/638280519245151237' -d 'D:/1-Images-A/Images-dd514b88-Results/A2)People/dd514b88/{2}'", + "problemMatcher": [] + }, + { + "label": "File-Folder-Helper AOT s VSCode Possible", + "type": "shell", + "command": "& L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net7.0/win-x64/publish/File-Folder-Helper.exe s V 'L:/Git/View-by-Distance-MKLink-Console/Instance'", "problemMatcher": [] } ] diff --git a/Shared/Models/LocationContainer.cs b/Shared/Models/LocationContainer.cs index 05a473f..67a7fec 100644 --- a/Shared/Models/LocationContainer.cs +++ b/Shared/Models/LocationContainer.cs @@ -2,5 +2,13 @@ using System.Drawing; namespace View_by_Distance.Shared.Models; -public record LocationContainer(bool FromDistanceContent, int? DirectoryNumber, string File, long PersonKey, int Id, int WholePercentages, IReadOnlyList Directories, RectangleF? Rectangle, Location? Location) -{ } \ No newline at end of file +public record LocationContainer(bool FromDistanceContent, + int? DirectoryNumber, + string File, + long PersonKey, + string DisplayDirectoryName, + int Id, + int WholePercentages, + IReadOnlyList Directories, + RectangleF? Rectangle, + Location? Location); \ No newline at end of file diff --git a/Shared/Models/Methods/IDistance.cs b/Shared/Models/Methods/IDistance.cs index edec0cc..5086d80 100644 --- a/Shared/Models/Methods/IDistance.cs +++ b/Shared/Models/Methods/IDistance.cs @@ -6,5 +6,6 @@ public interface IDistance { void ReviewLocationContainerDistanceTolerance(float locationContainerDistanceTolerance, ReadOnlyCollection> locationContainers); + void SaveMappedForOutputResolutions(string a2PeopleContentDirectory, string eDistanceContentDirectory, float[] rangeDistanceTolerance, ReadOnlyCollection> locationContainers, string personKeyFormatted, string? displayDirectoryName); } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IPerson.cs b/Shared/Models/Stateless/Methods/IPerson.cs index c99f9f3..1aa3e8e 100644 --- a/Shared/Models/Stateless/Methods/IPerson.cs +++ b/Shared/Models/Stateless/Methods/IPerson.cs @@ -40,7 +40,7 @@ public interface IPerson bool TestStatic_IsDefaultName(string personDisplayDirectoryName) => IsDefaultName(personDisplayDirectoryName); static bool IsDefaultName(string personDisplayDirectoryName) => - personDisplayDirectoryName.Length > 1 && personDisplayDirectoryName[0] == 'X' && personDisplayDirectoryName[1] == ']'; + personDisplayDirectoryName.Length > 1 && personDisplayDirectoryName[0] == 'X' && personDisplayDirectoryName[1] == '+'; bool TestStatic_IsDefaultName(Models.PersonContainer personContainer) => IsDefaultName(personContainer); diff --git a/Shared/Models/Stateless/Methods/ISorting.cs b/Shared/Models/Stateless/Methods/ISorting.cs index 1852258..b34b7c4 100644 --- a/Shared/Models/Stateless/Methods/ISorting.cs +++ b/Shared/Models/Stateless/Methods/ISorting.cs @@ -8,9 +8,9 @@ public interface ISorting static Models.Sorting[] Sort(List collection) => (from l in collection orderby l.DistancePermyriad, l.DaysDelta select l).ToArray(); - Models.Sorting TestStatic_Get(int faceDistancePermyriad, double rangeDistanceTolerance, Models.FaceDistance faceDistanceEncoding, Models.FaceDistance faceDistanceLength) => - Get(faceDistancePermyriad, rangeDistanceTolerance, faceDistanceEncoding, faceDistanceLength); - static Models.Sorting Get(int faceDistancePermyriad, double rangeDistanceTolerance, Models.FaceDistance faceDistanceEncoding, Models.FaceDistance faceDistanceLength) => - Sorting.Get(faceDistancePermyriad, rangeDistanceTolerance, faceDistanceEncoding, faceDistanceLength); + Models.Sorting TestStatic_Get(int faceDistancePermyriad, float distanceTolerance, Models.FaceDistance faceDistanceEncoding, Models.FaceDistance faceDistanceLength) => + Get(faceDistancePermyriad, distanceTolerance, faceDistanceEncoding, faceDistanceLength); + static Models.Sorting Get(int faceDistancePermyriad, float distanceTolerance, Models.FaceDistance faceDistanceEncoding, Models.FaceDistance faceDistanceLength) => + Sorting.Get(faceDistancePermyriad, distanceTolerance, faceDistanceEncoding, faceDistanceLength); } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/Sorting.cs b/Shared/Models/Stateless/Methods/Sorting.cs index 6508bc7..3b9688f 100644 --- a/Shared/Models/Stateless/Methods/Sorting.cs +++ b/Shared/Models/Stateless/Methods/Sorting.cs @@ -3,7 +3,7 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class Sorting { - internal static Models.Sorting Get(int faceDistancePermyriad, double rangeDistanceTolerance, Models.FaceDistance faceDistanceEncoding, Models.FaceDistance faceDistanceLength) + internal static Models.Sorting Get(int faceDistancePermyriad, float distanceTolerance, Models.FaceDistance faceDistanceEncoding, Models.FaceDistance faceDistanceLength) { Models.Sorting result; if (faceDistanceLength.Length is null) @@ -13,7 +13,7 @@ internal abstract class Sorting TimeSpan timeSpan = new(faceDistanceLength.DateTimeOriginalThenMinimumDateTime.Ticks - faceDistanceEncoding.DateTimeOriginalThenMinimumDateTime.Ticks); bool older = timeSpan.TotalMilliseconds < 0; int daysDelta = (int)Math.Round(Math.Abs(timeSpan.TotalDays), 0); - int distancePermyriad = (int)(faceDistanceLength.Length.Value / rangeDistanceTolerance * faceDistancePermyriad); + int distancePermyriad = (int)(faceDistanceLength.Length.Value / distanceTolerance * faceDistancePermyriad); result = new(daysDelta, distancePermyriad, faceDistanceLength.Id, older, faceDistanceLength.WholePercentages.Value); return result; }