Moved more to Map library
This commit is contained in:
		| @ -54,7 +54,8 @@ public class Compare | ||||
|         bool reverse = false; | ||||
|         string outputExtension = ".jpg"; | ||||
|         PredictorModel? predictorModel = null; | ||||
|         Map.Models.MapLogic mapLogic = new(_AppSettings.MaxDegreeOfParallelism, propertyConfiguration); | ||||
|         Dictionary<string, Shared.Models.Person> personKeyValuePairs = new(); | ||||
|         Map.Models.MapLogic mapLogic = new(_AppSettings.MaxDegreeOfParallelism, propertyConfiguration, outputExtension, personKeyValuePairs); | ||||
|         A_Property propertyLogic = GetPropertyLogic(reverse, model, outputExtension, predictorModel, mapLogic); | ||||
|         foreach (string spelling in configuration.Spelling) | ||||
|         { | ||||
|  | ||||
| @ -234,6 +234,7 @@ public class DlibDotNet | ||||
|             sourceDirectoryNames = Array.Empty<string>(); | ||||
|         else | ||||
|         { | ||||
|             string? century; | ||||
|             string argZero = Path.GetFullPath(args[0]); | ||||
|             sourceDirectoryNames = argZero.Split(Path.DirectorySeparatorChar); | ||||
|             if (!argZero.StartsWith(propertyConfiguration.RootDirectory)) | ||||
| @ -243,7 +244,8 @@ public class DlibDotNet | ||||
|                 if (!configuration.MixedYearRelativePaths.Contains(sourceDirectoryNames[0])) | ||||
|                 { | ||||
|                     string[] segments = sourceDirectoryNames[0].Split(' '); | ||||
|                     if (segments.Length < 2 || segments[^1].Length != 4 || (segments[^1][..2] != "19" && segments[^1][..2] != "20")) | ||||
|                     century = segments[^1].Length == 4 ? segments[^1][..2] : null; | ||||
|                     if (segments.Length < 2 || century is null || (century != "18" && century != "19" && century != "20")) | ||||
|                         throw new Exception("root subdirectory must have a year at the end or directory name needs to be added to the exclude list!"); | ||||
|                 } | ||||
|             } | ||||
| @ -362,7 +364,7 @@ public class DlibDotNet | ||||
|         string message = $"{container.R:000}.{container.G} / {containersCount:000}) {filteredItems.Length:000} file(s) - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}"; | ||||
|         using (ProgressBar progressBar = new(filteredItems.Length, message, options)) | ||||
|         { | ||||
|             _ = Parallel.For(0, filteredItems.Length, parallelOptions, i => | ||||
|             _ = Parallel.For(0, filteredItems.Length, parallelOptions, (i, state) => | ||||
|                { | ||||
|                    try | ||||
|                    { | ||||
| @ -640,6 +642,36 @@ public class DlibDotNet | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static List<(string, int, Mapping, DateTime, bool?, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>)> Convert(Dictionary<string, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>> keyValuePairs) | ||||
|     { | ||||
|         List<(string, int, Mapping, DateTime, bool?, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>)> results = new(); | ||||
|         MappingContainer mc; | ||||
|         foreach (KeyValuePair<string, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>> keyValuePair in keyValuePairs) | ||||
|         { | ||||
|             foreach ((FaceRecognitionDotNet.FaceEncoding _, MappingContainer mappingContainer) in keyValuePair.Value) | ||||
|             { | ||||
|                 mc = mappingContainer; | ||||
|                 results.Add(new(mc.Key, mc.Id, mc.Mapping, mc.MinimumDateTime, mc.IsWrongYear, keyValuePair.Value)); | ||||
|             } | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private static Dictionary<int, List<MappingContainer>> Strip(Dictionary<string, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>> keyValuePairs) | ||||
|     { | ||||
|         Dictionary<int, List<MappingContainer>> results = new(); | ||||
|         foreach (KeyValuePair<string, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>> keyValuePair in keyValuePairs) | ||||
|         { | ||||
|             foreach ((FaceRecognitionDotNet.FaceEncoding _, MappingContainer mappingContainer) in keyValuePair.Value) | ||||
|             { | ||||
|                 if (!results.ContainsKey(mappingContainer.Id)) | ||||
|                     results.Add(mappingContainer.Id, new()); | ||||
|                 results[mappingContainer.Id].Add(mappingContainer); | ||||
|             } | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private void Search(Property.Models.Configuration configuration, bool reverse, Model? model, PredictorModel? predictorModel, string argZero, Person[] people, bool isSilent) | ||||
|     { | ||||
|         if (_Log is null) | ||||
| @ -653,8 +685,8 @@ public class DlibDotNet | ||||
|         string eResultsFullGroupDirectory; | ||||
|         string zResultsFullGroupDirectory; | ||||
|         string d2ResultsFullGroupDirectory; | ||||
|         Dictionary<string, List<Person>> peopleCollection = A2_People.Convert(people); | ||||
|         Map.Models.MapLogic mapLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration); | ||||
|         Dictionary<string, Person> personKeyValuePairs = A2_People.Convert(people); | ||||
|         Map.Models.MapLogic mapLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FilenameExtension, personKeyValuePairs); | ||||
|         A_Property propertyLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FilenameExtension, reverse, model, predictorModel, mapLogic.IndicesFromNew, mapLogic.KeyValuePairs); | ||||
|         if (string.IsNullOrEmpty(configuration.RootDirectory)) | ||||
|             containers = A_Property.Get(configuration, propertyLogic); | ||||
| @ -669,30 +701,35 @@ public class DlibDotNet | ||||
|                 mapLogic.UseKeyValuePairsSaveFaceEncoding(containers); | ||||
|                 foreach (Container container in containers) | ||||
|                 { | ||||
|                     mapLogic.AddToNamed(container.Items); | ||||
|                     mapLogic.AddToMapping(container.Items); | ||||
|                     if (_Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution)) | ||||
|                         D_Face.SaveShortcuts(_Configuration.JuliePhares, dResultsFullGroupDirectory, ticks, peopleCollection, mapLogic, container.Items); | ||||
|                         mapLogic.SaveShortcuts(_Configuration.JuliePhares, dResultsFullGroupDirectory, ticks, container.Items); | ||||
|                 } | ||||
|                 mapLogic.SaveAllCollection(); | ||||
|                 if (_Configuration.SaveResizedSubfiles) | ||||
|                 { | ||||
|                     string dFacesContentDirectory; | ||||
|                     string eDistanceContentDirectory; | ||||
|                     string eDistanceCollectionDirectory; | ||||
|                     string zPropertyHolderContentDirectory; | ||||
|                     string zPropertyHolderSingletonDirectory; | ||||
|                     string zPropertyHolderCollectionDirectory; | ||||
|                     dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, "()"); | ||||
|                     eDistanceContentDirectory = Path.Combine(eResultsFullGroupDirectory, $"({ticks})"); | ||||
|                     zPropertyHolderSingletonDirectory = Path.Combine(zResultsFullGroupDirectory, "{}"); | ||||
|                     eDistanceCollectionDirectory = Path.Combine(eResultsFullGroupDirectory, $"[{ticks}]"); | ||||
|                     List<(DateTime, bool?, PersonBirthday, FaceRecognitionDotNet.FaceEncoding[])> collection; | ||||
|                     collection = E_Distance.ParallelWork(_AppSettings.MaxDegreeOfParallelism, argZero, mapLogic, containers); | ||||
|                     zPropertyHolderContentDirectory = Path.Combine(zResultsFullGroupDirectory, $"({ticks})"); | ||||
|                     zPropertyHolderCollectionDirectory = Path.Combine(zResultsFullGroupDirectory, $"[{ticks}]"); | ||||
|                     Dictionary<string, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>> keyValuePairs = _Distance.ParallelWork(_AppSettings.MaxDegreeOfParallelism, _Configuration.IgnoreRelativePaths, argZero, ticks, containers); | ||||
|                     _ = LogDeltaInSeconds(ticks, nameof(E_Distance.ParallelWork)); | ||||
|                     Dictionary<int, List<MappingContainer>> strippedKeyValuePairs = Strip(keyValuePairs); | ||||
|                     List<(string, int, Mapping, DateTime, bool?, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>)> collection = Convert(keyValuePairs); | ||||
|                     mapLogic.SaveMapping(argZero, containers, dFacesContentDirectory, d2ResultsFullGroupDirectory, zPropertyHolderContentDirectory); | ||||
|                     _ = LogDeltaInMinutes(ticks, nameof(mapLogic.SaveMapping)); | ||||
|                     _Distance.AddToFaceDistance(_AppSettings.MaxDegreeOfParallelism, argZero, ticks, mapLogic, containers, outputResolution, collection); | ||||
|                     _ = LogDeltaInSeconds(ticks, nameof(_Distance.AddToFaceDistance)); | ||||
|                     mapLogic.AddToClosest(_AppSettings.MaxDegreeOfParallelism, argZero, containers); | ||||
|                     _ = LogDeltaInMinutes(ticks, nameof(mapLogic.AddToClosest)); | ||||
|                     mapLogic.SaveClosest(argZero, containers, dFacesContentDirectory, d2ResultsFullGroupDirectory, zPropertyHolderContentDirectory); | ||||
|                     _ = LogDeltaInMinutes(ticks, nameof(mapLogic.SaveClosest)); | ||||
|                     E_Distance.SavePropertyHolders(argZero, containers, zPropertyHolderSingletonDirectory); | ||||
|                     _ = LogDeltaInSeconds(ticks, nameof(E_Distance.SavePropertyHolders)); | ||||
|                     E_Distance.SaveThreeSigmaFaceEncodings(collection, peopleCollection, eDistanceCollectionDirectory); | ||||
|                     _ = LogDeltaInSeconds(ticks, nameof(E_Distance.SaveThreeSigmaFaceEncodings)); | ||||
|                     E_Distance.SaveClosest(argZero, containers, peopleCollection, dFacesContentDirectory, d2ResultsFullGroupDirectory, eDistanceContentDirectory); | ||||
|                     _ = LogDeltaInMinutes(ticks, nameof(E_Distance.SaveClosest)); | ||||
|                 } | ||||
|                 if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Any()) | ||||
|                     break; | ||||
|  | ||||
| @ -73,16 +73,16 @@ internal class A2_People | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     internal static Dictionary<string, List<Person>> Convert(Person[] people) | ||||
|     internal static Dictionary<string, Person> Convert(Person[] people) | ||||
|     { | ||||
|         Dictionary<string, List<Person>> results = new(); | ||||
|         Dictionary<string, Person> results = new(); | ||||
|         string personKey; | ||||
|         foreach (Person person in people) | ||||
|         { | ||||
|             personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(person.Birthday); | ||||
|             if (!results.ContainsKey(personKey)) | ||||
|                 results.Add(personKey, new List<Person>()); | ||||
|             results[personKey].Add(person); | ||||
|             if (results.ContainsKey(personKey)) | ||||
|                 break; | ||||
|             results.Add(personKey, person); | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
| @ -153,7 +153,7 @@ internal class D2_FaceParts | ||||
|                 collection.Add(new(face, string.Empty, string.Empty)); | ||||
|                 continue; | ||||
|             } | ||||
|             deterministicHashCodeKey = Shared.Models.Stateless.Methods.INamed.GetDeterministicHashCodeKey(item, face); | ||||
|             deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); | ||||
|             fileInfo = new FileInfo(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FilenameExtension}")); | ||||
|             if (!fileInfo.Exists) | ||||
|             { | ||||
|  | ||||
| @ -2,14 +2,12 @@ using System.Drawing; | ||||
| using System.Drawing.Drawing2D; | ||||
| using System.Drawing.Imaging; | ||||
| using System.Text.Json; | ||||
| using System.Text.RegularExpressions; | ||||
| using View_by_Distance.FaceRecognitionDotNet; | ||||
| using View_by_Distance.Metadata.Models; | ||||
| using View_by_Distance.Property.Models; | ||||
| using View_by_Distance.Resize.Models; | ||||
| using View_by_Distance.Shared.Models; | ||||
| using View_by_Distance.Shared.Models.Stateless; | ||||
| using WindowsShortcutFactory; | ||||
|  | ||||
| namespace View_by_Distance.Instance.Models; | ||||
|  | ||||
| @ -346,7 +344,7 @@ public class D_Face | ||||
|                 collection.Add(new(face, string.Empty)); | ||||
|                 continue; | ||||
|             } | ||||
|             deterministicHashCodeKey = Shared.Models.Stateless.Methods.INamed.GetDeterministicHashCodeKey(item, face); | ||||
|             deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); | ||||
|             fileInfo = new FileInfo(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FilenameExtension}")); | ||||
|             if (!fileInfo.Exists) | ||||
|             { | ||||
| @ -368,60 +366,6 @@ public class D_Face | ||||
|             SaveFaces(item.ResizedFileHolder, collection); | ||||
|     } | ||||
|  | ||||
|     internal static void SaveShortcuts(string[] juliePhares, string dResultsFullGroupDirectory, long ticks, Dictionary<string, List<Person>> peopleCollection, Map.Models.MapLogic mapLogic, List<Item> items) | ||||
|     { | ||||
|         Person person; | ||||
|         string fileName; | ||||
|         string fullName; | ||||
|         DateTime? minimumDateTime; | ||||
|         WindowsShortcut windowsShortcut; | ||||
|         const string pattern = @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]"; | ||||
|         string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, $"({ticks})"); | ||||
|         List<(Item, (string, Face?, (string, string, string, string))[])> collections = mapLogic.GetCollection(items, dFacesContentDirectory); | ||||
|         foreach ((Item item, (string personKey, Face? _, (string, string, string, string))[] collection) in collections) | ||||
|         { | ||||
|             if (collection.Length != 1) | ||||
|                 continue; | ||||
|             foreach ((string personKey, Face? _, (string directory, string copyDirectory, string copyFileName, string shortcutFileName)) in collection) | ||||
|             { | ||||
|                 if (string.IsNullOrEmpty(personKey)) | ||||
|                     continue; | ||||
|                 if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) | ||||
|                     continue; | ||||
|                 minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); | ||||
|                 if (minimumDateTime is null) | ||||
|                     continue; | ||||
|                 if (!Directory.Exists(directory)) | ||||
|                 { | ||||
|                     _ = Directory.CreateDirectory(directory); | ||||
|                     if (!string.IsNullOrEmpty(personKey) && peopleCollection.ContainsKey(personKey)) | ||||
|                     { | ||||
|                         person = peopleCollection[personKey][0]; | ||||
|                         fullName = string.Concat(Regex.Replace(Shared.Models.Stateless.Methods.IPersonName.GetFullName(person.Name), pattern, string.Empty), ".txt"); | ||||
|                         File.WriteAllText(Path.Combine(directory, fullName), string.Empty); | ||||
|                     } | ||||
|                 } | ||||
|                 if (juliePhares.Contains(personKey) && !string.IsNullOrEmpty(copyDirectory)) | ||||
|                 { | ||||
|                     if (!Directory.Exists(copyDirectory)) | ||||
|                         _ = Directory.CreateDirectory(copyDirectory); | ||||
|                     fileName = Path.Combine(copyDirectory, $"{item.Property.Id.Value}{item.ResizedFileHolder.ExtensionLowered}"); | ||||
|                     if (!File.Exists(fileName)) | ||||
|                         File.Copy(item.ResizedFileHolder.FullName, fileName); | ||||
|                 } | ||||
|                 fileName = Path.Combine(directory, $"{item.Property.Id.Value}.lnk"); | ||||
|                 if (File.Exists(fileName)) | ||||
|                     continue; | ||||
|                 windowsShortcut = new() { Path = item.ImageFileHolder.FullName }; | ||||
|                 windowsShortcut.Save(fileName); | ||||
|                 windowsShortcut.Dispose(); | ||||
|                 if (!File.Exists(fileName)) | ||||
|                     continue; | ||||
|                 File.SetLastWriteTime(fileName, minimumDateTime.Value); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static bool HasLeftAndRight(Dictionary<string, FacePoint[]> faceParts) | ||||
|     { | ||||
|         bool result = true; | ||||
|  | ||||
| @ -1,13 +1,10 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.RegularExpressions; | ||||
| using View_by_Distance.FaceRecognitionDotNet; | ||||
| using View_by_Distance.Metadata.Models; | ||||
| using View_by_Distance.Property.Models; | ||||
| using View_by_Distance.Resize.Models; | ||||
| using View_by_Distance.Shared.Models; | ||||
| using View_by_Distance.Shared.Models.Properties; | ||||
| using View_by_Distance.Shared.Models.Stateless; | ||||
| using WindowsShortcutFactory; | ||||
|  | ||||
| namespace View_by_Distance.Instance.Models; | ||||
|  | ||||
| @ -424,6 +421,152 @@ internal class E_Distance | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static int GetSelectedIndex(int maxDegreeOfParallelism, Random random, List<FaceRecognitionDotNet.FaceEncoding> faceEncodings) | ||||
|     { | ||||
|         int? result; | ||||
|         int selectedIndex; | ||||
|         List<(int? Index, double? Sum)> faceDistanceCollections = new(); | ||||
|         if (maxDegreeOfParallelism == 1) | ||||
|         { | ||||
|             double sum; | ||||
|             List<double> faceDistances; | ||||
|             for (int i = 0; i < faceEncodings.Count; i++) | ||||
|             { | ||||
|                 faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncodings[i]); | ||||
|                 sum = faceDistances.Sum(); | ||||
|                 faceDistanceCollections.Add(new(i, sum)); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             for (int i = 0; i < faceEncodings.Count; i++) | ||||
|                 faceDistanceCollections.Add(new(null, null)); | ||||
|             ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; | ||||
|             _ = Parallel.For(0, faceEncodings.Count, parallelOptions, (i, state) => | ||||
|             { | ||||
|                 List<double> faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncodings[i]); | ||||
|                 double sum = faceDistances.Sum(); | ||||
|                 lock (faceDistanceCollections) | ||||
|                     faceDistanceCollections[i] = new(i, sum); | ||||
|             }); | ||||
|         } | ||||
|         faceDistanceCollections = faceDistanceCollections.OrderBy(l => l.Sum).ToList(); | ||||
|         if (faceDistanceCollections.Count != faceEncodings.Count) | ||||
|             throw new Exception(); | ||||
|         if (faceDistanceCollections.Count > 1000) | ||||
|             selectedIndex = random.Next(0, 36); | ||||
|         else if (faceDistanceCollections.Count > 500) | ||||
|             selectedIndex = random.Next(0, 31); | ||||
|         else if (faceDistanceCollections.Count > 200) | ||||
|             selectedIndex = random.Next(0, 26); | ||||
|         else if (faceDistanceCollections.Count > 100) | ||||
|             selectedIndex = random.Next(0, 21); | ||||
|         else if (faceDistanceCollections.Count > 50) | ||||
|             selectedIndex = random.Next(0, 16); | ||||
|         else if (faceDistanceCollections.Count > 25) | ||||
|             selectedIndex = random.Next(0, 11); | ||||
|         else if (faceDistanceCollections.Count > 10) | ||||
|             selectedIndex = random.Next(0, 6); | ||||
|         else if (faceDistanceCollections.Count > 5) | ||||
|             selectedIndex = random.Next(0, 3); | ||||
|         else | ||||
|             selectedIndex = 0; | ||||
|         result = faceDistanceCollections[selectedIndex].Index; | ||||
|         if (result is null) | ||||
|             throw new NullReferenceException(nameof(result)); | ||||
|         return result.Value; | ||||
|     } | ||||
|  | ||||
|     private static void SetFiltered(List<(FaceRecognitionDotNet.FaceEncoding FaceEncoding, MappingContainer MappingContainer)> collection) | ||||
|     { | ||||
|         double ucl; | ||||
|         bool check; | ||||
|         double average; | ||||
|         double[] doubles; | ||||
|         double standardDeviation; | ||||
|         double?[] nullableDoubles; | ||||
|         for (int i = 0; i < int.MaxValue; i++) | ||||
|         { | ||||
|             check = true; | ||||
|             nullableDoubles = (from l in collection where l.MappingContainer.Mapping.Filtered is not null && !l.MappingContainer.Mapping.Filtered.Value select l.MappingContainer.Distance).ToArray(); | ||||
|             doubles = (from l in nullableDoubles where l.HasValue select l.Value).ToArray(); | ||||
|             if (doubles.Length < 4) | ||||
|                 break; | ||||
|             average = doubles.Average(); | ||||
|             standardDeviation = GetStandardDeviation(doubles, average); | ||||
|             ucl = average + (standardDeviation * 3); | ||||
|             if (ucl > IFaceDistance.Tolerance) | ||||
|                 ucl = IFaceDistance.Tolerance; | ||||
|             foreach ((FaceRecognitionDotNet.FaceEncoding _, MappingContainer mappingContainer) in collection) | ||||
|             { | ||||
|                 if (mappingContainer.Mapping.Filtered is null || mappingContainer.Mapping.Filtered.Value || mappingContainer.Distance <= ucl) | ||||
|                     continue; | ||||
|                 if (check) | ||||
|                     check = false; | ||||
|                 mappingContainer.Mapping.SetFiltered(); | ||||
|             } | ||||
|             if (check) | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static FaceDistance GetFaceDistanceParallelFor(Face face, FaceRecognitionDotNet.FaceEncoding faceEncoding, Mapping mapping, DateTime minimumDateTime, bool? isWrongYear, string key, FaceRecognitionDotNet.FaceEncoding[] faceEncodings) | ||||
|     { | ||||
|         FaceDistance result; | ||||
|         if (face.Location?.NormalizedPixelPercentage is null) | ||||
|             throw new NullReferenceException(nameof(face.Location.NormalizedPixelPercentage)); | ||||
|         List<double> faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncoding); | ||||
|         result = new(faceDistances, isWrongYear, key, mapping, minimumDateTime); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static List<FaceDistance> GetFaceDistanceCollection(int maxDegreeOfParallelism, List<(string Key, int Id, Mapping Mapping, DateTime MinimumDateTime, bool? IsWrongYear, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>)> collection, Face face) | ||||
|     { | ||||
|         List<FaceDistance> results; | ||||
|         if (face.FaceEncoding is null) | ||||
|             throw new NullReferenceException(nameof(face.FaceEncoding)); | ||||
|         FaceRecognitionDotNet.FaceEncoding faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); | ||||
|         if (maxDegreeOfParallelism == 1) | ||||
|         { | ||||
|             results = new(); | ||||
|             FaceDistance faceDistance; | ||||
|             List<double> faceDistances; | ||||
|             FaceRecognitionDotNet.FaceEncoding[] faceEncodings; | ||||
|             if (face.Location?.NormalizedPixelPercentage is null) | ||||
|                 throw new NullReferenceException(nameof(face.Location.NormalizedPixelPercentage)); | ||||
|             foreach ((string key, int id, Mapping mapping, DateTime minimumDateTime, bool? isWrongYear, List<(FaceRecognitionDotNet.FaceEncoding FaceEncoding, MappingContainer _)> faceEncodingContainers) in collection) | ||||
|             { | ||||
|                 faceEncodings = (from l in faceEncodingContainers select l.FaceEncoding).ToArray(); | ||||
|                 faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncoding); | ||||
|                 faceDistance = new(faceDistances, isWrongYear, key, mapping, minimumDateTime); | ||||
|                 results.Add(faceDistance); | ||||
|                 if (results.Count > IFaceDistance.MaximumPer) | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             results = new(); | ||||
|             ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; | ||||
|             _ = Parallel.For(0, collection.Count, parallelOptions, (i, state) => | ||||
|             { | ||||
|                 (string key, int id, Mapping mapping, DateTime minimumDateTime, bool? isWrongYear, List<(FaceRecognitionDotNet.FaceEncoding FaceEncoding, MappingContainer _)> faceEncodingContainers) = collection[i]; | ||||
|                 FaceRecognitionDotNet.FaceEncoding[] faceEncodings = (from l in faceEncodingContainers select l.FaceEncoding).ToArray(); | ||||
|                 FaceDistance? closest = GetFaceDistanceParallelFor(face, faceEncoding, mapping, minimumDateTime, isWrongYear, key, faceEncodings); | ||||
|                 if (closest is not null) | ||||
|                 { | ||||
|                     lock (results) | ||||
|                     { | ||||
|                         results.Add(closest); | ||||
|                         if (results.Count > IFaceDistance.MaximumPer) | ||||
|                             state.Break(); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private static FaceRecognitionDotNet.FaceEncoding? GetFaceEncoding(Face face) | ||||
|     { | ||||
|         FaceRecognitionDotNet.FaceEncoding? result; | ||||
| @ -434,24 +577,18 @@ internal class E_Distance | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static (int index, double sum) GetIndexAndSum(int i, List<FaceRecognitionDotNet.FaceEncoding> results) | ||||
|     { | ||||
|         List<double> faceDistances = FaceRecognition.FaceDistances(results, results[i]); | ||||
|         return new(i, faceDistances.Sum()); | ||||
|     } | ||||
|  | ||||
|     private static List<FaceRecognitionDotNet.FaceEncoding> GetFaceEncodings(int maxDegreeOfParallelism, List<(DateTime MinimumDateTime, bool? IsWrongYear, PersonBirthday PersonBirthday, Face Face)> collection) | ||||
|     private static List<FaceRecognitionDotNet.FaceEncoding> GetFaceEncodingsOnly(int maxDegreeOfParallelism, List<MappingContainer> collection) | ||||
|     { | ||||
|         List<FaceRecognitionDotNet.FaceEncoding> results; | ||||
|         if (maxDegreeOfParallelism == 1) | ||||
|         { | ||||
|             results = new(); | ||||
|             FaceRecognitionDotNet.FaceEncoding faceEncoding; | ||||
|             foreach ((DateTime _, bool? _, PersonBirthday _, Face face) in collection) | ||||
|             foreach (MappingContainer mappingContainer in collection) | ||||
|             { | ||||
|                 if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) | ||||
|                 if (mappingContainer.Face?.FaceEncoding is null || mappingContainer.Face.Location?.NormalizedPixelPercentage is null) | ||||
|                     continue; | ||||
|                 faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); | ||||
|                 faceEncoding = FaceRecognition.LoadFaceEncoding(mappingContainer.Face.FaceEncoding.RawEncoding); | ||||
|                 results.Add(faceEncoding); | ||||
|             } | ||||
|         } | ||||
| @ -459,9 +596,12 @@ internal class E_Distance | ||||
|         { | ||||
|             results = new(); | ||||
|             ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; | ||||
|             _ = Parallel.For(0, collection.Count, parallelOptions, i => | ||||
|             _ = Parallel.For(0, collection.Count, parallelOptions, (i, state) => | ||||
|             { | ||||
|                 FaceRecognitionDotNet.FaceEncoding? faceEncoding = GetFaceEncoding(collection[i].Face); | ||||
|                 Face? face = collection[i].Face; | ||||
|                 if (face is null) | ||||
|                     throw new Exception(); | ||||
|                 FaceRecognitionDotNet.FaceEncoding? faceEncoding = GetFaceEncoding(face); | ||||
|                 if (faceEncoding is not null) | ||||
|                 { | ||||
|                     lock (results) | ||||
| @ -469,136 +609,68 @@ internal class E_Distance | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|         if (collection.Count == results.Count && results.Count > 1) | ||||
|         { | ||||
|             double sum; | ||||
|             int lowestIndex; | ||||
|             double lowestSum; | ||||
|             List<double> faceDistances; | ||||
|             if (maxDegreeOfParallelism == 1) | ||||
|             { | ||||
|                 lowestIndex = 0; | ||||
|                 lowestSum = double.MaxValue; | ||||
|                 for (int i = 0; i < results.Count; i++) | ||||
|                 { | ||||
|                     faceDistances = FaceRecognition.FaceDistances(results, results[i]); | ||||
|                     sum = faceDistances.Sum(); | ||||
|                     if (sum >= lowestSum) | ||||
|                         continue; | ||||
|                     lowestIndex = i; | ||||
|                     lowestSum = sum; | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 List<(int Index, double Sum)> indicesAndSums = new(); | ||||
|                 ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; | ||||
|                 _ = Parallel.For(0, results.Count, parallelOptions, i => | ||||
|                 { | ||||
|                     (int index, double sum) = GetIndexAndSum(i, results); | ||||
|                     lock (indicesAndSums) | ||||
|                         indicesAndSums.Add(new(index, sum)); | ||||
|                 }); | ||||
|                 (lowestIndex, lowestSum) = (from l in indicesAndSums orderby l.Sum select l).First(); | ||||
|             } | ||||
|             faceDistances = FaceRecognition.FaceDistances(results, results[lowestIndex]); | ||||
|             sum = faceDistances.Sum(); | ||||
|             if (sum == lowestSum) | ||||
|             { | ||||
|                 double average = faceDistances.Average(); | ||||
|                 double standardDeviation = GetStandardDeviation(faceDistances, average); | ||||
|                 double lcl = average - (standardDeviation * 3); | ||||
|                 double ucl = average + (standardDeviation * 3); | ||||
|                 for (int i = results.Count - 1; i > -1; i--) | ||||
|                 { | ||||
|                     if (faceDistances[i] < lcl || faceDistances[i] > ucl) | ||||
|                         results.RemoveAt(i); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private static List<(DateTime, bool?, PersonBirthday, FaceRecognitionDotNet.FaceEncoding[])> GetThreeSigmaFaceEncodings(int maxDegreeOfParallelism, Dictionary<string, List<(DateTime, bool?, PersonBirthday, Face)>> keyValuePairs) | ||||
|     private Dictionary<string, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>> GetThreeSigmaFaceEncodings(int maxDegreeOfParallelism, long ticks, Dictionary<string, List<MappingContainer>> keyValuePairs) | ||||
|     { | ||||
|         List<(DateTime, bool?, PersonBirthday, FaceRecognitionDotNet.FaceEncoding[])> results = new(); | ||||
|         const int zero = 0; | ||||
|         if (_Log is null) | ||||
|             throw new NullReferenceException(nameof(_Log)); | ||||
|         Dictionary<string, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>> results = new(); | ||||
|         int totalSeconds; | ||||
|         int selectedIndex; | ||||
|         Random random = new(); | ||||
|         List<double> faceDistances; | ||||
|         MappingContainer mappingContainer; | ||||
|         int keyValuePairsCount = keyValuePairs.Count; | ||||
|         FaceRecognitionDotNet.FaceEncoding faceEncoding; | ||||
|         List<FaceRecognitionDotNet.FaceEncoding> faceEncodings; | ||||
|         foreach (KeyValuePair<string, List<(DateTime MinimumDateTime, bool? IsWrongYear, PersonBirthday PersonBirthday, Face _)>> keyValuePair in keyValuePairs) | ||||
|         List<(FaceRecognitionDotNet.FaceEncoding FaceEncoding, MappingContainer MappingContainer)> collection; | ||||
|         foreach (KeyValuePair<string, List<MappingContainer>> keyValuePair in keyValuePairs) | ||||
|         { | ||||
|             faceEncodings = GetFaceEncodings(maxDegreeOfParallelism, keyValuePair.Value); | ||||
|             results.Add(new(keyValuePair.Value[zero].MinimumDateTime, keyValuePair.Value[zero].IsWrongYear, keyValuePair.Value[zero].PersonBirthday, faceEncodings.ToArray())); | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private static Closest? GetClosestParallelFor(DateTime minimumDateTime, bool? isWrongYear, Face face, FaceRecognitionDotNet.FaceEncoding faceEncoding, (DateTime MinimumDateTime, bool? IsWrongYear, PersonBirthday PersonBirthday, FaceRecognitionDotNet.FaceEncoding[] FaceEncodings) tuple) | ||||
|     { | ||||
|         Closest? result; | ||||
|         if (isWrongYear.HasValue && !isWrongYear.Value && minimumDateTime < tuple.PersonBirthday.Value) | ||||
|             result = null; | ||||
|         else | ||||
|         { | ||||
|             List<double> faceDistances = FaceRecognition.FaceDistances(tuple.FaceEncodings, faceEncoding); | ||||
|             result = new(face.Location?.NormalizedPixelPercentage, tuple.MinimumDateTime, tuple.IsWrongYear, tuple.PersonBirthday, faceDistances); | ||||
|             if (result.Minimum > Shared.Models.Stateless.IClosest.MaximumMinimum) | ||||
|                 result = null; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static Closest[] GetClosestCollection(int maxDegreeOfParallelism, List<(DateTime, bool?, PersonBirthday, FaceRecognitionDotNet.FaceEncoding[])> collection, DateTime itemMinimumDateTime, bool? itemIsWrongYear, Face face) | ||||
|     { | ||||
|         Closest[] results; | ||||
|         List<Closest> closestCollection; | ||||
|         if (face.FaceEncoding is null) | ||||
|             throw new NullReferenceException(nameof(face.FaceEncoding)); | ||||
|         FaceRecognitionDotNet.FaceEncoding faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); | ||||
|         if (maxDegreeOfParallelism == 1) | ||||
|         { | ||||
|             closestCollection = new(); | ||||
|             Closest closest; | ||||
|             List<double> faceDistances; | ||||
|             foreach ((DateTime minimumDateTime, bool? isWrongYear, PersonBirthday personBirthday, FaceRecognitionDotNet.FaceEncoding[] faceEncodings) in collection) | ||||
|             collection = new(); | ||||
|             faceEncodings = GetFaceEncodingsOnly(maxDegreeOfParallelism, keyValuePair.Value); | ||||
|             for (int i = 0; i < faceEncodings.Count; i++) | ||||
|             { | ||||
|                 if (itemIsWrongYear.HasValue && !itemIsWrongYear.Value && itemMinimumDateTime < personBirthday.Value) | ||||
|                     continue; | ||||
|                 faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncoding); | ||||
|                 closest = new(face.Location?.NormalizedPixelPercentage, minimumDateTime, isWrongYear, personBirthday, faceDistances); | ||||
|                 if (closest.Minimum > Shared.Models.Stateless.IClosest.MaximumMinimum) | ||||
|                     continue; | ||||
|                 closestCollection.Add(closest); | ||||
|                 faceEncoding = faceEncodings[i]; | ||||
|                 mappingContainer = keyValuePair.Value[i]; | ||||
|                 collection.Add(new(faceEncoding, mappingContainer)); | ||||
|             } | ||||
|             results.Add(keyValuePair.Key, collection); | ||||
|             if (faceEncodings.Count == 1) | ||||
|                 selectedIndex = 0; | ||||
|             else | ||||
|                 selectedIndex = GetSelectedIndex(maxDegreeOfParallelism, random, faceEncodings); | ||||
|             faceDistances = FaceRecognition.FaceDistances(faceEncodings, faceEncodings[selectedIndex]); | ||||
|             for (int i = 0; i < faceEncodings.Count; i++) | ||||
|                 collection[i].MappingContainer.SetDistance(faceDistances[i]); | ||||
|             if (collection.Count > 1) | ||||
|                 SetFiltered(collection); | ||||
|             totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); | ||||
|             _Log.Information($"{keyValuePairsCount:0000}) {totalSeconds} total second(s) - {keyValuePair.Key} - {collection[selectedIndex].MappingContainer.Mapping.DisplayDirectoryName}"); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             closestCollection = new(); | ||||
|             ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; | ||||
|             _ = Parallel.For(0, collection.Count, parallelOptions, i => | ||||
|             { | ||||
|                 Closest? closest = GetClosestParallelFor(itemMinimumDateTime, itemIsWrongYear, face, faceEncoding, collection[i]); | ||||
|                 if (closest is not null) | ||||
|                 { | ||||
|                     lock (closestCollection) | ||||
|                         closestCollection.Add(closest); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|         results = Shared.Models.Stateless.Methods.IClosest.Get(closestCollection); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private static void AddClosest(int maxDegreeOfParallelism, string argZero, Map.Models.MapLogic mapLogic, List<Container> containers, List<(DateTime, bool?, PersonBirthday, FaceRecognitionDotNet.FaceEncoding[])> collection) | ||||
|     internal Dictionary<string, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>> ParallelWork(int maxDegreeOfParallelism, string[] ignoreRelativePaths, string argZero, long ticks, List<Container> containers) | ||||
|     { | ||||
|         string key; | ||||
|         Dictionary<string, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>> results; | ||||
|         Dictionary<string, List<MappingContainer>> keyValuePairs = Map.Models.Stateless.IMapLogic.GetKeyValuePairs(ignoreRelativePaths, argZero, containers); | ||||
|         results = GetThreeSigmaFaceEncodings(maxDegreeOfParallelism, ticks, keyValuePairs); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     public void AddToFaceDistance(int maxDegreeOfParallelism, string argZero, long ticks, Map.Models.MapLogic mapLogic, List<Container> containers, string outputResolution, List<(string, int, Mapping, DateTime, bool?, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>)> collection) | ||||
|     { | ||||
|         if (_Log is null) | ||||
|             throw new NullReferenceException(nameof(_Log)); | ||||
|         Face face; | ||||
|         Closest closest; | ||||
|         string personKey; | ||||
|         bool? itemIsWrongYear; | ||||
|         Closest[] closestCollection; | ||||
|         DateTime? itemMinimumDateTime; | ||||
|         string message; | ||||
|         int totalSeconds; | ||||
|         double deterministicHashCodeKey; | ||||
|         Dictionary<string, int> results = new(); | ||||
|         DateTime dateTime = DateTime.Now; | ||||
|         List<FaceDistance> faceDistances; | ||||
|         int containersCount = containers.Count; | ||||
|         foreach (Container container in containers) | ||||
|         { | ||||
|             if (!container.Items.Any()) | ||||
| @ -607,55 +679,29 @@ internal class E_Distance | ||||
|                 continue; | ||||
|             foreach (Item item in container.Items) | ||||
|             { | ||||
|                 if (item.ImageFileHolder is null || item.Property is null || item.Named.Any()) | ||||
|                 if (item.ImageFileHolder is null || item.Property?.Id is null) | ||||
|                     continue; | ||||
|                 itemMinimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); | ||||
|                 if (itemMinimumDateTime is null) | ||||
|                     continue; | ||||
|                 (itemIsWrongYear, _) = Map.Models.MapLogic.IsWrongYear(item); | ||||
|                 if (Shared.Models.Stateless.IClosest.SkipIsWrongYear && itemIsWrongYear.HasValue && itemIsWrongYear.Value) | ||||
|                     continue; | ||||
|                 item.Closest.Clear(); | ||||
|                 for (int i = 0; i < item.Faces.Count; i++) | ||||
|                 { | ||||
|                     face = item.Faces[i]; | ||||
|                     closest = new(face.Location?.NormalizedPixelPercentage, itemMinimumDateTime.Value, itemIsWrongYear); | ||||
|                     item.Closest.Add(closest); | ||||
|                     face.FaceDistances.Clear(); | ||||
|                     if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) | ||||
|                         continue; | ||||
|                     deterministicHashCodeKey = Shared.Models.Stateless.Methods.INamed.GetDeterministicHashCodeKey(item, face); | ||||
|                     closestCollection = GetClosestCollection(maxDegreeOfParallelism, collection, itemMinimumDateTime.Value, itemIsWrongYear, face); | ||||
|                     for (int j = 0; j < closestCollection.Length; j++) | ||||
|                     { | ||||
|                         closest = closestCollection[j]; | ||||
|                         if (closest.PersonBirthday is null) | ||||
|                             continue; | ||||
|                         personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(closest.PersonBirthday); | ||||
|                         if (mapLogic.IsIncorrect(deterministicHashCodeKey, personKey)) | ||||
|                             continue; | ||||
|                         key = Map.Models.MapLogic.GetKey(closest.MinimumDateTime, closest.IsWrongYear, closest.PersonBirthday); | ||||
|                         if (!results.ContainsKey(key)) | ||||
|                             results.Add(key, 0); | ||||
|                         else if (results[key] > Shared.Models.Stateless.IClosest.MaximumPer) | ||||
|                             continue; | ||||
|                         results[key] += 1; | ||||
|                         item.Closest[0] = closest; | ||||
|                         break; | ||||
|                     } | ||||
|                     if ((from l in item.Mapping where l.NormalizedPixelPercentage.HasValue && l.NormalizedPixelPercentage.Value == face.Location.NormalizedPixelPercentage.Value select true).Any()) | ||||
|                         continue; | ||||
|                     deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); | ||||
|                     if (mapLogic.Skip(deterministicHashCodeKey)) | ||||
|                         continue; | ||||
|                     faceDistances = GetFaceDistanceCollection(maxDegreeOfParallelism, collection, face); | ||||
|                     face.FaceDistances.AddRange(faceDistances); | ||||
|                 } | ||||
|             } | ||||
|             totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); | ||||
|             message = $"{container.R:000}.{container.G} / {containersCount:000}) {container.Items.Count:000} file(s) - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}"; | ||||
|             _Log.Information(message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal static List<(DateTime, bool?, PersonBirthday, FaceRecognitionDotNet.FaceEncoding[])> ParallelWork(int maxDegreeOfParallelism, string argZero, Map.Models.MapLogic mapLogic, List<Container> containers) | ||||
|     { | ||||
|         List<(DateTime, bool?, PersonBirthday, FaceRecognitionDotNet.FaceEncoding[])> results; | ||||
|         Dictionary<string, List<(DateTime, bool?, PersonBirthday, Face)>> keyValuePairs = Map.Models.MapLogic.GetKeyValuePairs(argZero, containers); | ||||
|         results = GetThreeSigmaFaceEncodings(maxDegreeOfParallelism, keyValuePairs); | ||||
|         AddClosest(maxDegreeOfParallelism, argZero, mapLogic, containers, results); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     public static void SavePropertyHolders(string argZero, List<Container> containers, string zPropertyHolderSingletonDirectory) | ||||
|     { | ||||
|         string json; | ||||
| @ -672,8 +718,6 @@ internal class E_Distance | ||||
|             { | ||||
|                 if (item.ImageFileHolder is null || item.Property is null || !item.Faces.Any() || !item.Closest.Any()) | ||||
|                     continue; | ||||
|                 if (!(from l in item.Closest where l.Average.HasValue select true).Any()) | ||||
|                     continue; | ||||
|                 json = JsonSerializer.Serialize(item, jsonSerializerOptions); | ||||
|                 fileInfo = new(string.Concat(zPropertyHolderSingletonDirectory, item.RelativePath, ".json")); | ||||
|                 if (fileInfo.Directory is null) | ||||
| @ -685,140 +729,4 @@ internal class E_Distance | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal static void SaveThreeSigmaFaceEncodings(List<(DateTime, bool?, PersonBirthday, FaceRecognitionDotNet.FaceEncoding[])> collection, Dictionary<string, List<Person>> peopleCollection, string eDistanceCollectionDirectory) | ||||
|     { | ||||
|         string json; | ||||
|         string checkFile; | ||||
|         string personKey; | ||||
|         string directory; | ||||
|         List<double[]> rawEncodings; | ||||
|         Person person; | ||||
|         const string facePopulatedKey = "ThreeSigma"; | ||||
|         const string pattern = @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]"; | ||||
|         foreach ((DateTime minimumDateTime, bool? isWrongYear, PersonBirthday personBirthday, FaceRecognitionDotNet.FaceEncoding[] faceEncodings) in collection) | ||||
|         { | ||||
|             rawEncodings = new(); | ||||
|             checkFile = string.Empty; | ||||
|             personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); | ||||
|             directory = Map.Models.MapLogic.GetDirectory(eDistanceCollectionDirectory, facePopulatedKey, minimumDateTime, isWrongYear, personBirthday, personKey); | ||||
|             if (!peopleCollection.ContainsKey(personKey)) | ||||
|                 continue; | ||||
|             person = peopleCollection[personKey][0]; | ||||
|             checkFile = string.Concat(directory, " - ", Regex.Replace(Shared.Models.Stateless.Methods.IPersonName.GetFullName(person.Name), pattern, string.Empty), ".json"); | ||||
|             if (string.IsNullOrEmpty(checkFile)) | ||||
|                 continue; | ||||
|             if (!Directory.Exists(directory)) | ||||
|                 _ = Directory.CreateDirectory(directory); | ||||
|             for (int i = 0; i < faceEncodings.Length; i++) | ||||
|                 rawEncodings.Add(faceEncodings[i].GetRawEncoding()); | ||||
|             json = JsonSerializer.Serialize(rawEncodings, new JsonSerializerOptions { WriteIndented = true }); | ||||
|             _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(checkFile, json, updateDateWhenMatches: true, compareBeforeWrite: true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal static List<(IFileHolder? resizedFileHolder, string directory, FileInfo? faceFileInfo, string checkFile, string shortcutFile)> GetClosest(string argZero, List<Container> containers, Dictionary<string, List<Person>> peopleCollection, string dFacesContentDirectory, string d2ResultsFullGroupDirectory, string eDistanceContentDirectory) | ||||
|     { | ||||
|         List<(IFileHolder?, string, FileInfo?, string, string)> results = new(); | ||||
|         Person person; | ||||
|         string checkFile; | ||||
|         string directory; | ||||
|         string personKey; | ||||
|         string personName; | ||||
|         string shortcutFile; | ||||
|         FileInfo faceFileInfo; | ||||
|         string? directoryName; | ||||
|         string facesDirectory; | ||||
|         string personDirectory; | ||||
|         FileInfo landmarkFileInfo; | ||||
|         string landmarksDirectory; | ||||
|         double deterministicHashCodeKey; | ||||
|         const string facePopulatedKey = nameof(Closest); | ||||
|         const string pattern = @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]"; | ||||
|         foreach (Container container in containers) | ||||
|         { | ||||
|             if (!container.Items.Any()) | ||||
|                 continue; | ||||
|             if (!container.SourceDirectory.StartsWith(argZero)) | ||||
|                 continue; | ||||
|             foreach (Item item in container.Items) | ||||
|             { | ||||
|                 if (item.ImageFileHolder is null || item.Property?.Id is null || item.ResizedFileHolder is null || item.Named.Any()) | ||||
|                     continue; | ||||
|                 if (!item.Closest.Any()) | ||||
|                     continue; | ||||
|                 directoryName = Path.GetDirectoryName(item.RelativePath); | ||||
|                 if (directoryName is null) | ||||
|                     throw new Exception(); | ||||
|                 foreach (Closest closest in item.Closest) | ||||
|                 { | ||||
|                     if (closest.Average is null || closest.NormalizedPixelPercentage is null || closest.PersonBirthday is null) | ||||
|                         continue; | ||||
|                     personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(closest.PersonBirthday); | ||||
|                     directory = Map.Models.MapLogic.GetDirectory(eDistanceContentDirectory, facePopulatedKey, closest.MinimumDateTime, closest.IsWrongYear, closest.PersonBirthday, personKey); | ||||
|                     if (!peopleCollection.ContainsKey(personKey)) | ||||
|                         personDirectory = string.Empty; | ||||
|                     else | ||||
|                     { | ||||
|                         person = peopleCollection[personKey][0]; | ||||
|                         personName = Shared.Models.Stateless.Methods.IPersonName.GetFullName(person.Name); | ||||
|                         personDirectory = Path.Combine(directory, Regex.Replace(personName, pattern, string.Empty), "lnk"); | ||||
|                         results.Add(new(null, personDirectory, null, string.Empty, string.Empty)); | ||||
|                     } | ||||
|                     facesDirectory = string.Concat(dFacesContentDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); | ||||
|                     landmarksDirectory = string.Concat(d2ResultsFullGroupDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); | ||||
|                     deterministicHashCodeKey = Shared.Models.Stateless.Methods.INamed.GetDeterministicHashCodeKey(item, closest); | ||||
|                     checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}"); | ||||
|                     faceFileInfo = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.png")); | ||||
|                     landmarkFileInfo = new(Path.Combine(landmarksDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.gif")); | ||||
|                     if (string.IsNullOrEmpty(personDirectory)) | ||||
|                         shortcutFile = string.Empty; | ||||
|                     else | ||||
|                         shortcutFile = Path.Combine(personDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.lnk"); | ||||
|                     results.Add(new(item.ResizedFileHolder, directory, faceFileInfo, checkFile, shortcutFile)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     internal static void SaveClosest(string argZero, List<Container> containers, Dictionary<string, List<Person>> peopleCollection, string dFacesContentDirectory, string d2ResultsFullGroupDirectory, string eDistanceContentDirectory) | ||||
|     { | ||||
|         WindowsShortcut windowsShortcut; | ||||
|         List<(IFileHolder? resizedFileHolder, string directory, FileInfo? faceFileInfo, string checkFile, string shortcutFile)> collection = GetClosest(argZero, containers, peopleCollection, dFacesContentDirectory, d2ResultsFullGroupDirectory, eDistanceContentDirectory); | ||||
|         string[] directories = (from l in collection select l.directory).Distinct().ToArray(); | ||||
|         foreach (string directory in directories) | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(directory)) | ||||
|                 continue; | ||||
|             if (!Directory.Exists(directory)) | ||||
|                 _ = Directory.CreateDirectory(directory); | ||||
|         } | ||||
|         foreach ((IFileHolder? resizedFileHolder, string directory, FileInfo? faceFileInfo, string checkFile, string shortcutFile) in collection) | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(directory) || string.IsNullOrEmpty(checkFile) || resizedFileHolder is null || faceFileInfo is null) | ||||
|                 continue; | ||||
|             if (File.Exists(checkFile)) | ||||
|                 continue; | ||||
|             if (faceFileInfo.Directory is not null && faceFileInfo.Directory.Exists && faceFileInfo.Exists) | ||||
|                 File.Copy(faceFileInfo.FullName, checkFile); | ||||
|             else | ||||
|                 File.Copy(resizedFileHolder.FullName, checkFile); | ||||
|         } | ||||
|         foreach ((IFileHolder? resizedFileHolder, string directory, FileInfo? _, string checkFile, string shortcutFile) in collection) | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(directory) || string.IsNullOrEmpty(checkFile) || resizedFileHolder is null) | ||||
|                 continue; | ||||
|             if (string.IsNullOrEmpty(shortcutFile) || !resizedFileHolder.Exists) | ||||
|                 continue; | ||||
|             try | ||||
|             { | ||||
|                 windowsShortcut = new() { Path = resizedFileHolder.FullName }; | ||||
|                 windowsShortcut.Save(shortcutFile); | ||||
|                 windowsShortcut.Dispose(); | ||||
|             } | ||||
|             catch (Exception) | ||||
|             { } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -38,6 +38,7 @@ | ||||
|     </ItemGroup> | ||||
|     <ItemGroup> | ||||
|         <PackageReference Include="System.Text.Json" Version="6.0.0" /> | ||||
|         <PackageReference Include="WindowsShortcutFactory" Version="1.0.1" /> | ||||
|     </ItemGroup> | ||||
|     <ItemGroup> | ||||
|         <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> | ||||
|  | ||||
| @ -1,28 +1,34 @@ | ||||
| using System.Diagnostics; | ||||
| using System.Text.Json; | ||||
| using View_by_Distance.Property.Models; | ||||
| using View_by_Distance.Shared.Models; | ||||
| using View_by_Distance.Shared.Models.Properties; | ||||
| using WindowsShortcutFactory; | ||||
|  | ||||
| namespace View_by_Distance.Map.Models; | ||||
|  | ||||
| public class MapLogic | ||||
| { | ||||
|  | ||||
|     protected readonly List<double> _SkipCollection; | ||||
|     protected readonly List<(int, string[])> _AllCollection; | ||||
|     protected readonly Dictionary<int, int[]> _KeyValuePairs; | ||||
|     protected readonly Dictionary<int, int[]> _IndicesFromNew; | ||||
|     protected readonly string _DeterministicHashCodeRootDirectory; | ||||
|     protected readonly string _DeterministicHashCodeContentDirectory; | ||||
|     protected readonly Dictionary<int, string[]> _SixCharacterNamedFaceInfo; | ||||
|     protected readonly Dictionary<int, string[]> _DeterministicHashCodeUnknownFaceKeyValuePairs; | ||||
|     protected readonly Dictionary<double, string[]> _DeterministicHashCodeKeyValuePairs; | ||||
|     protected readonly Dictionary<int, string[]> _DeterministicHashCodeUnknownFaceKeyValuePairs; | ||||
|     protected readonly Dictionary<double, string[]> _IncorrectDeterministicHashCodeKeyValuePairs; | ||||
|     protected readonly Dictionary<string, (string DisplayDirectoryName, int? ApproximateYears, string Key, PersonBirthday[] PersonBirthdays)> _PeopleKeyValuePairs; | ||||
|  | ||||
|     public Dictionary<int, int[]> KeyValuePairs => _KeyValuePairs; | ||||
|     public Dictionary<int, int[]> IndicesFromNew => _IndicesFromNew; | ||||
|  | ||||
|     private readonly Serilog.ILogger? _Log; | ||||
|     private readonly string _OutputExtension; | ||||
|     private readonly Configuration _Configuration; | ||||
|  | ||||
|     public MapLogic(int maxDegreeOfParallelism, Configuration configuration) | ||||
|     public MapLogic(int maxDegreeOfParallelism, Configuration configuration, string outputExtension, Dictionary<string, Person> personKeyValuePairs) | ||||
|     { | ||||
|         _AllCollection = new(); | ||||
|         _Configuration = configuration; | ||||
| @ -33,17 +39,20 @@ public class MapLogic | ||||
|         string json; | ||||
|         string[] files; | ||||
|         string fullPath; | ||||
|         _OutputExtension = outputExtension; | ||||
|         List<double> skipCollection = new(); | ||||
|         Dictionary<int, int[]>? keyValuePairs; | ||||
|         string deterministicHashCodeRootDirectory; | ||||
|         List<KeyValuePair<int, int[]>>? collection; | ||||
|         string deterministicHashCodeContentDirectory; | ||||
|         Dictionary<int, int[]> indicesFromNew = new(); | ||||
|         Dictionary<int, string[]>? sixCharacterNamedFaceInfo; | ||||
|         Dictionary<double, string[]> deterministicHashCodeKeyValuePairs = new(); | ||||
|         Dictionary<double, string[]> incorrectDeterministicHashCodeKeyValuePairs = new(); | ||||
|         string? rootDirectoryParent = Path.GetDirectoryName(configuration.RootDirectory); | ||||
|         Dictionary<string, (string, int?, string, PersonBirthday[])> peopleKeyValuePairs = new(); | ||||
|         if (string.IsNullOrEmpty(rootDirectoryParent)) | ||||
|             throw new NullReferenceException(nameof(rootDirectoryParent)); | ||||
|         files = Directory.GetFiles(rootDirectoryParent, "*DeterministicHashCode*.json", SearchOption.TopDirectoryOnly); | ||||
|         files = Directory.GetFiles(rootDirectoryParent, "DeterministicHashCode*.json", SearchOption.TopDirectoryOnly); | ||||
|         if (files.Length != 1) | ||||
|             deterministicHashCodeUnknownFaceKeyValuePairs = new(); | ||||
|         else | ||||
| @ -53,15 +62,11 @@ public class MapLogic | ||||
|             if (deterministicHashCodeUnknownFaceKeyValuePairs is null) | ||||
|                 throw new NullReferenceException(nameof(deterministicHashCodeUnknownFaceKeyValuePairs)); | ||||
|         } | ||||
|         string[] directories = Directory.GetDirectories(rootDirectoryParent, "*DeterministicHashCode*", SearchOption.TopDirectoryOnly); | ||||
|         string[] directories = Directory.GetDirectories(rootDirectoryParent, "DeterministicHashCode", SearchOption.TopDirectoryOnly); | ||||
|         if (!directories.Any()) | ||||
|             deterministicHashCodeRootDirectory = string.Empty; | ||||
|             deterministicHashCodeContentDirectory = string.Empty; | ||||
|         else | ||||
|         { | ||||
|             Dictionary<int, List<Shared.Models.Face>> faces = new(); | ||||
|             deterministicHashCodeRootDirectory = directories[0]; | ||||
|             SetKeyValuePairs(deterministicHashCodeRootDirectory, deterministicHashCodeKeyValuePairs, incorrectDeterministicHashCodeKeyValuePairs, faces); | ||||
|         } | ||||
|             deterministicHashCodeContentDirectory = Stateless.ByDeterministicHashCode.SetByRef(_OutputExtension, personKeyValuePairs, skipCollection, peopleKeyValuePairs, deterministicHashCodeUnknownFaceKeyValuePairs, deterministicHashCodeKeyValuePairs, incorrectDeterministicHashCodeKeyValuePairs, directories[0]); | ||||
|         if (!deterministicHashCodeUnknownFaceKeyValuePairs.Any()) | ||||
|             sixCharacterNamedFaceInfo = new(); | ||||
|         else | ||||
| @ -77,7 +82,7 @@ public class MapLogic | ||||
|                     throw new NullReferenceException(nameof(sixCharacterNamedFaceInfo)); | ||||
|             } | ||||
|         } | ||||
|         files = Directory.GetFiles(rootDirectoryParent, "*keyValuePairs*.json", SearchOption.TopDirectoryOnly); | ||||
|         files = Directory.GetFiles(rootDirectoryParent, "*keyValuePairs-6*.json", SearchOption.TopDirectoryOnly); | ||||
|         if (files.Length != 1) | ||||
|             keyValuePairs = new(); | ||||
|         else | ||||
| @ -107,116 +112,80 @@ public class MapLogic | ||||
|         } | ||||
|         _KeyValuePairs = keyValuePairs; | ||||
|         _IndicesFromNew = indicesFromNew; | ||||
|         _SkipCollection = skipCollection; | ||||
|         _PeopleKeyValuePairs = peopleKeyValuePairs; | ||||
|         _SixCharacterNamedFaceInfo = sixCharacterNamedFaceInfo; | ||||
|         _DeterministicHashCodeRootDirectory = deterministicHashCodeRootDirectory; | ||||
|         _DeterministicHashCodeKeyValuePairs = deterministicHashCodeKeyValuePairs; | ||||
|         _DeterministicHashCodeContentDirectory = deterministicHashCodeContentDirectory; | ||||
|         _IncorrectDeterministicHashCodeKeyValuePairs = incorrectDeterministicHashCodeKeyValuePairs; | ||||
|         _DeterministicHashCodeUnknownFaceKeyValuePairs = deterministicHashCodeUnknownFaceKeyValuePairs; | ||||
|     } | ||||
|  | ||||
|     public bool IsIncorrect(double deterministicHashCodeKey, string personKey) => _DeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey) && _IncorrectDeterministicHashCodeKeyValuePairs[deterministicHashCodeKey].Contains(personKey); | ||||
|     public bool Skip(double deterministicHashCodeKey) => _SkipCollection.Contains(deterministicHashCodeKey); | ||||
|  | ||||
|     private void SetKeyValuePairs(string deterministicHashCodeRootDirectory, List<(string, double)> named, List<(string, double)> incorrect, Dictionary<int, List<Shared.Models.Face>> keyValuePairs) | ||||
|     public void SaveShortcuts(string[] juliePhares, string dResultsFullGroupDirectory, long ticks, List<Item> items) | ||||
|     { | ||||
|         string[] files; | ||||
|         string personKey; | ||||
|         string[] yearDirectories; | ||||
|         string ticksDirectoryName; | ||||
|         string[] personKeyDirectories; | ||||
|         string[] personNameDirectories; | ||||
|         string[] personNameLinkDirectories; | ||||
|         double? reversedDeterministicHashCodeKey; | ||||
|         bool keyValuePairsAny = keyValuePairs.Any(); | ||||
|         string[] ticksDirectories = Directory.GetDirectories(deterministicHashCodeRootDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|         foreach (string ticksDirectory in ticksDirectories) | ||||
|         string fileName; | ||||
|         string fullName; | ||||
|         DateTime? minimumDateTime; | ||||
|         WindowsShortcut windowsShortcut; | ||||
|         (string DisplayDirectoryName, int? ApproximateYears, string Key, PersonBirthday[] _) person; | ||||
|         string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, $"({ticks})"); | ||||
|         List<(Item, (string, Face?, (string, string, string, string))[])> collections = GetCollection(items, dFacesContentDirectory); | ||||
|         foreach ((Item item, (string personKey, Face? _, (string, string, string, string))[] collection) in collections) | ||||
|         { | ||||
|             ticksDirectoryName = Path.GetFileName(ticksDirectory); | ||||
|             if (ticksDirectoryName.Length < 3 || ticksDirectoryName[0] != '(' || ticksDirectoryName[^1] != ')') | ||||
|             if (collection.Length != 1) | ||||
|                 continue; | ||||
|             personKeyDirectories = Directory.GetDirectories(ticksDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|             foreach (string personKeyDirectory in personKeyDirectories) | ||||
|             foreach ((string personKey, Face? _, (string directory, string copyDirectory, string copyFileName, string shortcutFileName)) in collection) | ||||
|             { | ||||
|                 personKey = Path.GetFileName(personKeyDirectory); | ||||
|                 if (personKey == nameof(Shared.Models.Closest)) | ||||
|                     throw new Exception($"Move personKey directories up one from {nameof(Shared.Models.Closest)} and delete {nameof(Shared.Models.Closest)} directory!"); | ||||
|                 yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                 foreach (string yearDirectory in yearDirectories) | ||||
|                 if (string.IsNullOrEmpty(personKey)) | ||||
|                     continue; | ||||
|                 if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null) | ||||
|                     continue; | ||||
|                 minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); | ||||
|                 if (minimumDateTime is null) | ||||
|                     continue; | ||||
|                 if (!Directory.Exists(directory)) | ||||
|                 { | ||||
|                     files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                     personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                     foreach (string file in files) | ||||
|                         File.Delete(file); | ||||
|                     foreach (string personNameDirectory in personNameDirectories) | ||||
|                     _ = Directory.CreateDirectory(directory); | ||||
|                     if (!string.IsNullOrEmpty(personKey) && _PeopleKeyValuePairs.ContainsKey(personKey)) | ||||
|                     { | ||||
|                         files = Directory.GetFiles(personNameDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                         personNameLinkDirectories = Directory.GetDirectories(personNameDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                         foreach (string file in files) | ||||
|                         { | ||||
|                             if (file.EndsWith(".lnk") || file.EndsWith(".json")) | ||||
|                                 continue; | ||||
|                             reversedDeterministicHashCodeKey = Shared.Models.Stateless.Methods.INamed.GetReversedDeterministicHashCodeKey(keyValuePairsAny, keyValuePairs, file); | ||||
|                             if (reversedDeterministicHashCodeKey is null) | ||||
|                                 continue; | ||||
|                             named.Add(new(personKey, reversedDeterministicHashCodeKey.Value)); | ||||
|                         } | ||||
|                         foreach (string personNameLinkDirectory in personNameLinkDirectories) | ||||
|                         { | ||||
|                             files = Directory.GetFiles(personNameLinkDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                             foreach (string file in files) | ||||
|                             { | ||||
|                                 if (!file.EndsWith(".lnk")) | ||||
|                                     continue; | ||||
|                                 File.Delete(file); | ||||
|                             } | ||||
|                             _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(personNameLinkDirectory); | ||||
|                         } | ||||
|                         _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(personNameDirectory); | ||||
|                         person = _PeopleKeyValuePairs[personKey]; | ||||
|                         fullName = string.Concat(person.DisplayDirectoryName, ".txt"); | ||||
|                         File.WriteAllText(Path.Combine(directory, fullName), string.Empty); | ||||
|                     } | ||||
|                     _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(yearDirectory); | ||||
|                 } | ||||
|                 _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(personKeyDirectory); | ||||
|                 if (juliePhares.Contains(personKey) && !string.IsNullOrEmpty(copyDirectory)) | ||||
|                 { | ||||
|                     if (!Directory.Exists(copyDirectory)) | ||||
|                         _ = Directory.CreateDirectory(copyDirectory); | ||||
|                     fileName = Path.Combine(copyDirectory, $"{item.Property.Id.Value}{item.ResizedFileHolder.ExtensionLowered}"); | ||||
|                     if (!File.Exists(fileName)) | ||||
|                         File.Copy(item.ResizedFileHolder.FullName, fileName); | ||||
|                 } | ||||
|                 fileName = Path.Combine(directory, $"{item.Property.Id.Value}.lnk"); | ||||
|                 if (File.Exists(fileName)) | ||||
|                     continue; | ||||
|                 windowsShortcut = new() { Path = item.ImageFileHolder.FullName }; | ||||
|                 windowsShortcut.Save(fileName); | ||||
|                 windowsShortcut.Dispose(); | ||||
|                 if (!File.Exists(fileName)) | ||||
|                     continue; | ||||
|                 File.SetLastWriteTime(fileName, minimumDateTime.Value); | ||||
|             } | ||||
|             _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(ticksDirectory); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void SetKeyValuePairs(string deterministicHashCodeRootDirectory, Dictionary<double, string[]> deterministicHashCodeKeyValuePairs, Dictionary<double, string[]> incorrectDeterministicHashCodeKeyValuePairs, Dictionary<int, List<Shared.Models.Face>> keyValuePairs) | ||||
|     public void UseKeyValuePairsSaveFaceEncoding(List<Container> containers) | ||||
|     { | ||||
|         Dictionary<double, List<string>> namedKeyValuePairs = new(); | ||||
|         Dictionary<double, List<string>> incorrectKeyValuePairs = new(); | ||||
|         List<(string PersonKey, double IdAndNormalizedPixelPercentage)> named = new(); | ||||
|         List<(string PersonKey, double IdAndNormalizedPixelPercentage)> incorrect = new(); | ||||
|         SetKeyValuePairs(deterministicHashCodeRootDirectory, named, incorrect, keyValuePairs); | ||||
|         named = (from l in named orderby l.IdAndNormalizedPixelPercentage select l).ToList(); | ||||
|         incorrect = (from l in incorrect orderby l.IdAndNormalizedPixelPercentage select l).ToList(); | ||||
|         foreach ((string personKey, double idAndNormalizedPixelPercentage) in named) | ||||
|         if (!string.IsNullOrEmpty(_DeterministicHashCodeContentDirectory)) | ||||
|         { | ||||
|             if (!namedKeyValuePairs.ContainsKey(idAndNormalizedPixelPercentage)) | ||||
|                 namedKeyValuePairs.Add(idAndNormalizedPixelPercentage, new()); | ||||
|             namedKeyValuePairs[idAndNormalizedPixelPercentage].Add(personKey); | ||||
|         } | ||||
|         foreach ((string personKey, double idAndNormalizedPixelPercentage) in incorrect) | ||||
|         { | ||||
|             if (!incorrectKeyValuePairs.ContainsKey(idAndNormalizedPixelPercentage)) | ||||
|                 incorrectKeyValuePairs.Add(idAndNormalizedPixelPercentage, new()); | ||||
|             incorrectKeyValuePairs[idAndNormalizedPixelPercentage].Add(personKey); | ||||
|         } | ||||
|         foreach (KeyValuePair<double, List<string>> keyValuePair in namedKeyValuePairs) | ||||
|             deterministicHashCodeKeyValuePairs.Add(keyValuePair.Key, keyValuePair.Value.Distinct().ToArray()); | ||||
|         foreach (KeyValuePair<double, List<string>> keyValuePair in incorrectKeyValuePairs) | ||||
|             incorrectDeterministicHashCodeKeyValuePairs.Add(keyValuePair.Key, keyValuePair.Value.Distinct().ToArray()); | ||||
|     } | ||||
|  | ||||
|     public void UseKeyValuePairsSaveFaceEncoding(List<Shared.Models.Container> containers) | ||||
|     { | ||||
|         if (!string.IsNullOrEmpty(_DeterministicHashCodeRootDirectory)) | ||||
|         { | ||||
|             Dictionary<int, List<Shared.Models.Face>> keyValuePairs = new(); | ||||
|             List<(string PersonKey, double IdAndNormalizedPixelPercentage)> named = new(); | ||||
|             List<(string PersonKey, double IdAndNormalizedPixelPercentage)> incorrect = new(); | ||||
|             foreach (Shared.Models.Container container in containers) | ||||
|             Dictionary<int, List<Face>> keyValuePairs = new(); | ||||
|             List<(string PersonKey, double IdAndNormalizedPixelPercentage)> deterministicHashCodeCollection = new(); | ||||
|             List<(string PersonKey, double IdAndNormalizedPixelPercentage)> incorrectDeterministicHashCodeCollection = new(); | ||||
|             foreach (Container container in containers) | ||||
|             { | ||||
|                 foreach (Shared.Models.Item item in container.Items) | ||||
|                 foreach (Item item in container.Items) | ||||
|                 { | ||||
|                     if (item.ImageFileHolder is null || item.Property?.Id is null || !item.Faces.Any()) | ||||
|                         continue; | ||||
| @ -229,7 +198,7 @@ public class MapLogic | ||||
|                     keyValuePairs.Add(item.Property.Id.Value, item.Faces); | ||||
|                 } | ||||
|             } | ||||
|             SetKeyValuePairs(_DeterministicHashCodeRootDirectory, named, incorrect, keyValuePairs); | ||||
|             Stateless.ByDeterministicHashCode.SetKeyValuePairs(_DeterministicHashCodeContentDirectory, deterministicHashCodeCollection, incorrectDeterministicHashCodeCollection, keyValuePairs); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -250,12 +219,12 @@ public class MapLogic | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public void AddToMapLogicAllCollection(Shared.Models.Item[] filteredItems) | ||||
|     public void AddToMapLogicAllCollection(Item[] filteredItems) | ||||
|     { | ||||
|         if (_SixCharacterNamedFaceInfo.Any()) | ||||
|         { | ||||
|             string[] keys; | ||||
|             Shared.Models.Item item; | ||||
|             Item item; | ||||
|             for (int i = 0; i < filteredItems.Length; i++) | ||||
|             { | ||||
|                 item = filteredItems[i]; | ||||
| @ -301,61 +270,91 @@ public class MapLogic | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void AddToNamed(List<Shared.Models.Item> items) | ||||
|     public void AddToMapping(List<Item> items) | ||||
|     { | ||||
|         bool? isWrongYear; | ||||
|         DateTime minimumDateTime; | ||||
|         Mapping mapping; | ||||
|         string personKey; | ||||
|         int? approximateYears; | ||||
|         string displayDirectoryName; | ||||
|         PersonBirthday? personBirthday; | ||||
|         double deterministicHashCodeKey; | ||||
|         List<string> personKeys = new(); | ||||
|         Shared.Models.PersonBirthday? personBirthday; | ||||
|         foreach (Shared.Models.Item item in items) | ||||
|         (string DisplayDirectoryName, int? ApproximateYears, string Key, PersonBirthday[] PersonBirthdays) person; | ||||
|         foreach (Item item in items) | ||||
|         { | ||||
|             if (item.ImageFileHolder is null) | ||||
|                 continue; | ||||
|             if (item.Property?.Id is null || item.ResizedFileHolder is null) | ||||
|                 continue; | ||||
|             foreach (Shared.Models.Face face in item.Faces) | ||||
|             foreach (Face face in item.Faces) | ||||
|             { | ||||
|                 personKeys.Clear(); | ||||
|                 if (face.LocationIndex is null) | ||||
|                 if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) | ||||
|                     continue; | ||||
|                 deterministicHashCodeKey = Shared.Models.Stateless.Methods.INamed.GetDeterministicHashCodeKey(item, face); | ||||
|                 deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); | ||||
|                 if (!_DeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey)) | ||||
|                     continue; | ||||
|                 minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); | ||||
|                 personKeys.AddRange(_DeterministicHashCodeKeyValuePairs[deterministicHashCodeKey]); | ||||
|                 (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); | ||||
|                 for (int i = 0; i < personKeys.Count; i++) | ||||
|                 { | ||||
|                     personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKeys[i]); | ||||
|                     if (_PeopleKeyValuePairs.ContainsKey(personKeys[i])) | ||||
|                     { | ||||
|                         person = _PeopleKeyValuePairs[personKeys[i]]; | ||||
|                         personBirthday = person.PersonBirthdays[0]; | ||||
|                         approximateYears = person.ApproximateYears; | ||||
|                         displayDirectoryName = person.DisplayDirectoryName; | ||||
|                         personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         approximateYears = null; | ||||
|                         personKey = personKeys[i]; | ||||
|                         displayDirectoryName = "_ _ _"; | ||||
|                         personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKey); | ||||
|                     } | ||||
|                     if (personBirthday is null) | ||||
|                         continue; | ||||
|                     if (face.Location is null) | ||||
|                         continue; | ||||
|                     item.Named.Add(new(isWrongYear, minimumDateTime, face.Location.NormalizedPixelPercentage, personBirthday)); | ||||
|                     mapping = new(approximateYears, displayDirectoryName, face.Location.NormalizedPixelPercentage, personBirthday, personKey); | ||||
|                     item.Mapping.Add(mapping); | ||||
|                 } | ||||
|             } | ||||
|             if (!personKeys.Any()) | ||||
|             if (Shared.Models.Stateless.IMapping.UseDeterministicHashCodeUnknownFaceKeyValuePairsForAddToMapping && !personKeys.Any()) | ||||
|             { | ||||
|                 if (!_DeterministicHashCodeUnknownFaceKeyValuePairs.ContainsKey(item.Property.Id.Value)) | ||||
|                     continue; | ||||
|                 minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); | ||||
|                 personKeys.AddRange(_DeterministicHashCodeUnknownFaceKeyValuePairs[item.Property.Id.Value]); | ||||
|                 (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); | ||||
|                 for (int i = 0; i < personKeys.Count; i++) | ||||
|                 { | ||||
|                     personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKeys[i]); | ||||
|                     if (_PeopleKeyValuePairs.ContainsKey(personKeys[i])) | ||||
|                     { | ||||
|                         person = _PeopleKeyValuePairs[personKeys[i]]; | ||||
|                         personBirthday = person.PersonBirthdays[0]; | ||||
|                         approximateYears = person.ApproximateYears; | ||||
|                         displayDirectoryName = person.DisplayDirectoryName; | ||||
|                         personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         approximateYears = null; | ||||
|                         personKey = personKeys[i]; | ||||
|                         displayDirectoryName = "_ _ _"; | ||||
|                         personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKey); | ||||
|                     } | ||||
|                     if (personBirthday is null) | ||||
|                         continue; | ||||
|                     item.Named.Add(new(isWrongYear, minimumDateTime, personBirthday)); | ||||
|                     mapping = new(approximateYears, displayDirectoryName, personBirthday, personKey); | ||||
|                     item.Mapping.Add(mapping); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public List<(Shared.Models.Item, (string, Shared.Models.Face?, (string, string, string, string))[])> GetCollection(List<Shared.Models.Item> items, string dFacesContentDirectory) | ||||
|     public List<(Item, (string, Face?, (string, string, string, string))[])> GetCollection(List<Item> items, string dFacesContentDirectory) | ||||
|     { | ||||
|         List<(Shared.Models.Item, (string, Shared.Models.Face?, (string, string, string, string))[])> results = new(); | ||||
|         List<(Item, (string, Face?, (string, string, string, string))[])> results = new(); | ||||
|         int years; | ||||
|         Face face; | ||||
|         Item item; | ||||
|         string[] keys; | ||||
|         string directory; | ||||
|         string personKey; | ||||
| @ -366,15 +365,14 @@ public class MapLogic | ||||
|         string copyDirectory; | ||||
|         string? relativePath; | ||||
|         string isWrongYearFlag; | ||||
|         Shared.Models.Face face; | ||||
|         string shortcutFileName; | ||||
|         Shared.Models.Item item; | ||||
|         string subDirectoryName; | ||||
|         List<int> indices = new(); | ||||
|         DateTime? minimumDateTime; | ||||
|         List<Shared.Models.Face> faceCollection; | ||||
|         Shared.Models.PersonBirthday? personBirthday; | ||||
|         List<(string, Shared.Models.Face?, (string, string, string, string))> collection; | ||||
|         DateTime dateTime = DateTime.Now; | ||||
|         List<Face> faceCollection; | ||||
|         PersonBirthday? personBirthday; | ||||
|         List<(string, Face?, (string, string, string, string))> collection; | ||||
|         for (int i = 0; i < items.Count; i++) | ||||
|         { | ||||
|             indices.Clear(); | ||||
| @ -433,7 +431,10 @@ public class MapLogic | ||||
|                         if (timeSpan.Value.Ticks < 0) | ||||
|                             subDirectoryName = "!---"; | ||||
|                         else | ||||
|                             subDirectoryName = $"^{Math.Floor(timeSpan.Value.TotalDays / 365):000}"; | ||||
|                         { | ||||
|                             (years, _) = Shared.Models.Stateless.Methods.IPersonBirthday.GetAge(dateTime, personBirthday); | ||||
|                             subDirectoryName = $"^{years:000}"; | ||||
|                         } | ||||
|                     } | ||||
|                     face = faceCollection[zero]; | ||||
|                     directory = Path.Combine(dFacesContentDirectory, "Shortcuts", personKey, subDirectoryName); | ||||
| @ -457,71 +458,304 @@ public class MapLogic | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     public static string GetKey(DateTime minimumDateTime, bool? isWrongYear, Shared.Models.PersonBirthday personBirthday) | ||||
|     public void AddToClosest(int maxDegreeOfParallelism, string argZero, List<Container> containers) | ||||
|     { | ||||
|         string result; | ||||
|         string personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); | ||||
|         TimeSpan? timeSpan = Shared.Models.Stateless.Methods.IPersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear, personBirthday); | ||||
|         if (timeSpan.HasValue && timeSpan.Value.Ticks < 0) | ||||
|             result = string.Concat(personKey, "!---"); | ||||
|         else if (timeSpan.HasValue) | ||||
|             result = string.Concat(personKey, $"^{Math.Floor(timeSpan.Value.TotalDays / 365):000}"); | ||||
|         else | ||||
|         { | ||||
|             string isWrongYearFlag = Shared.Models.Stateless.Methods.IItem.GetWrongYearFlag(isWrongYear); | ||||
|             result = string.Concat(personKey, $"{isWrongYearFlag}{minimumDateTime:yyyy}"); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public static string GetDirectory(string directory, string subDirectory, DateTime minimumDateTime, bool? isWrongYear, Shared.Models.PersonBirthday personBirthday, string personKey) | ||||
|     { | ||||
|         string result; | ||||
|         TimeSpan? timeSpan = Shared.Models.Stateless.Methods.IPersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear, personBirthday); | ||||
|         if (timeSpan.HasValue && timeSpan.Value.Ticks < 0) | ||||
|             result = Path.Combine(directory, subDirectory, personKey, "!---"); | ||||
|         else if (timeSpan.HasValue) | ||||
|             result = Path.Combine(directory, subDirectory, personKey, $"^{Math.Floor(timeSpan.Value.TotalDays / 365):000}"); | ||||
|         else | ||||
|         { | ||||
|             string isWrongYearFlag = Shared.Models.Stateless.Methods.IItem.GetWrongYearFlag(isWrongYear); | ||||
|             result = Path.Combine(directory, subDirectory, personKey, $"{isWrongYearFlag}{minimumDateTime:yyyy}"); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public static Dictionary<string, List<(DateTime, bool?, Shared.Models.PersonBirthday, Shared.Models.Face)>> GetKeyValuePairs(string argZero, List<Shared.Models.Container> containers) | ||||
|     { | ||||
|         Dictionary<string, List<(DateTime, bool?, Shared.Models.PersonBirthday, Shared.Models.Face)>> results = new(); | ||||
|         string key; | ||||
|         foreach (Shared.Models.Container container in containers) | ||||
|         string dateKey; | ||||
|         Closest closest; | ||||
|         DateTime minimumDateTime; | ||||
|         Closest[] closestCollection; | ||||
|         double deterministicHashCodeKey; | ||||
|         DateTime dateTime = DateTime.Now; | ||||
|         Dictionary<string, int> keyValuePairs = new(); | ||||
|         foreach (Container container in containers) | ||||
|         { | ||||
|             if (!container.Items.Any()) | ||||
|                 continue; | ||||
|             if (!container.SourceDirectory.StartsWith(argZero)) | ||||
|                 continue; | ||||
|             foreach (Shared.Models.Item item in container.Items) | ||||
|             foreach (Item item in container.Items) | ||||
|             { | ||||
|                 if (item.ImageFileHolder is null || item.Property is null || !item.Named.Any()) | ||||
|                 if (item.ImageFileHolder is null || item.Property is null) | ||||
|                     continue; | ||||
|                 foreach (Shared.Models.Named named in item.Named) | ||||
|                 foreach (Face face in item.Faces) | ||||
|                 { | ||||
|                     if (named.NormalizedPixelPercentage is null && (item.Named.Count != 1 || item.Faces.Count != 1)) | ||||
|                     if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) | ||||
|                         continue; | ||||
|                     foreach (Shared.Models.Face face in item.Faces) | ||||
|                     deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); | ||||
|                     if (_DeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey)) | ||||
|                         continue; | ||||
|                     minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); | ||||
|                     closestCollection = Shared.Models.Stateless.Methods.IClosest.GetCollection(face, minimumDateTime, face.FaceDistances); | ||||
|                     face.FaceDistances.Clear(); | ||||
|                     for (int j = 0; j < closestCollection.Length; j++) | ||||
|                     { | ||||
|                         closest = closestCollection[j]; | ||||
|                         if (_IncorrectDeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey) && _IncorrectDeterministicHashCodeKeyValuePairs[deterministicHashCodeKey].Contains(closest.Mapping.PersonKey)) | ||||
|                             continue; | ||||
|                         dateKey = Stateless.MapLogic.GetDateKey(dateTime, closest.Mapping, closest.MinimumDateTime, closest.IsWrongYear); | ||||
|                         key = string.Concat(closest.Mapping.PersonKey, dateKey); | ||||
|                         if (!keyValuePairs.ContainsKey(key)) | ||||
|                             keyValuePairs.Add(key, 0); | ||||
|                         else if (keyValuePairs[key] > Shared.Models.Stateless.IClosest.MaximumPer) | ||||
|                             continue; | ||||
|                         keyValuePairs[key] += 1; | ||||
|                         item.Closest.Add(closest); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private List<(IFileHolder? resizedFileHolder, string directory, FileInfo? faceFileInfo, string checkFile, string shortcutFile, string json)> GetClosest(string argZero, List<Container> containers, string dFacesContentDirectory, string d2ResultsFullGroupDirectory, string zPropertyHolderContentDirectory) | ||||
|     { | ||||
|         List<(IFileHolder?, string, FileInfo?, string, string, string)> results = new(); | ||||
|         Closest? match; | ||||
|         string dateKey; | ||||
|         string checkFile; | ||||
|         string directory; | ||||
|         string shortcutFile; | ||||
|         FileInfo faceFileInfo; | ||||
|         string? directoryName; | ||||
|         string facesDirectory; | ||||
|         string personDirectory; | ||||
|         List<int> used = new(); | ||||
|         FileInfo landmarkFileInfo; | ||||
|         string landmarksDirectory; | ||||
|         double deterministicHashCodeKey; | ||||
|         DateTime dateTime = DateTime.Now; | ||||
|         const string facePopulatedKey = nameof(Closest); | ||||
|         foreach (Container container in containers) | ||||
|         { | ||||
|             if (!container.Items.Any()) | ||||
|                 continue; | ||||
|             if (!container.SourceDirectory.StartsWith(argZero)) | ||||
|                 continue; | ||||
|             foreach (Item item in container.Items) | ||||
|             { | ||||
|                 used.Clear(); | ||||
|                 if (item.ImageFileHolder is null || item.Property?.Id is null || item.ResizedFileHolder is null) | ||||
|                     continue; | ||||
|                 if (!item.Closest.Any()) | ||||
|                     continue; | ||||
|                 directoryName = Path.GetDirectoryName(item.RelativePath); | ||||
|                 if (directoryName is null) | ||||
|                     throw new Exception(); | ||||
|                 foreach (Face face in item.Faces) | ||||
|                 { | ||||
|                     match = null; | ||||
|                     if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) | ||||
|                         continue; | ||||
|                     foreach (Closest closest in item.Closest) | ||||
|                     { | ||||
|                         if (closest.NormalizedPixelPercentage != face.Location.NormalizedPixelPercentage.Value) | ||||
|                             continue; | ||||
|                         match = closest; | ||||
|                         break; | ||||
|                     } | ||||
|                     if (match is null) | ||||
|                         continue; | ||||
|                     foreach (Mapping mapping in item.Mapping) | ||||
|                     { | ||||
|                         if (mapping.NormalizedPixelPercentage is null || mapping.NormalizedPixelPercentage.Value != face.Location.NormalizedPixelPercentage.Value) | ||||
|                             continue; | ||||
|                         throw new Exception(); | ||||
|                     } | ||||
|                     dateKey = Stateless.MapLogic.GetDateKey(dateTime, match.Mapping, match.MinimumDateTime, match.IsWrongYear); | ||||
|                     directory = Path.Combine(zPropertyHolderContentDirectory, facePopulatedKey, match.Mapping.PersonKey, dateKey); | ||||
|                     personDirectory = Path.Combine(directory, match.Mapping.DisplayDirectoryName[..1], "lnk"); | ||||
|                     results.Add(new(null, personDirectory, null, string.Empty, string.Empty, string.Empty)); | ||||
|                     facesDirectory = string.Concat(dFacesContentDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); | ||||
|                     landmarksDirectory = string.Concat(d2ResultsFullGroupDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); | ||||
|                     deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); | ||||
|                     checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}"); | ||||
|                     faceFileInfo = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.png")); | ||||
|                     landmarkFileInfo = new(Path.Combine(landmarksDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.gif")); | ||||
|                     if (string.IsNullOrEmpty(personDirectory)) | ||||
|                         shortcutFile = string.Empty; | ||||
|                     else | ||||
|                         shortcutFile = Path.Combine(personDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.lnk"); | ||||
|                     results.Add(new(item.ResizedFileHolder, directory, faceFileInfo, checkFile, shortcutFile, string.Empty)); | ||||
|                     personDirectory = Path.Combine(directory, match.Mapping.DisplayDirectoryName[..1], "lnk", match.Mapping.DisplayDirectoryName); | ||||
|                     results.Add(new(null, personDirectory, null, string.Empty, string.Empty, string.Empty)); | ||||
|                     used.Add(face.Location.NormalizedPixelPercentage.Value); | ||||
|                 } | ||||
|                 foreach (Closest closest in item.Closest) | ||||
|                 { | ||||
|                     if (used.Contains(closest.NormalizedPixelPercentage)) | ||||
|                         continue; | ||||
|                     dateKey = Stateless.MapLogic.GetDateKey(dateTime, closest.Mapping, closest.MinimumDateTime, closest.IsWrongYear); | ||||
|                     directory = Path.Combine(zPropertyHolderContentDirectory, facePopulatedKey, closest.Mapping.PersonKey, dateKey); | ||||
|                     personDirectory = Path.Combine(directory, closest.Mapping.DisplayDirectoryName, "lnk"); | ||||
|                     results.Add(new(null, personDirectory, null, string.Empty, string.Empty, string.Empty)); | ||||
|                     facesDirectory = string.Concat(dFacesContentDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); | ||||
|                     landmarksDirectory = string.Concat(d2ResultsFullGroupDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); | ||||
|                     deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, closest); | ||||
|                     checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}"); | ||||
|                     faceFileInfo = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.png")); | ||||
|                     landmarkFileInfo = new(Path.Combine(landmarksDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.gif")); | ||||
|                     if (string.IsNullOrEmpty(personDirectory)) | ||||
|                         shortcutFile = string.Empty; | ||||
|                     else | ||||
|                         shortcutFile = Path.Combine(personDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.lnk"); | ||||
|                     results.Add(new(item.ResizedFileHolder, directory, faceFileInfo, checkFile, shortcutFile, string.Empty)); | ||||
|                     personDirectory = Path.Combine(directory, closest.Mapping.DisplayDirectoryName[..1], "lnk", closest.Mapping.DisplayDirectoryName); | ||||
|                     results.Add(new(null, personDirectory, null, string.Empty, string.Empty, string.Empty)); | ||||
|                     used.Add(closest.NormalizedPixelPercentage); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     private List<(IFileHolder? resizedFileHolder, string directory, FileInfo? faceFileInfo, string checkFile, string shortcutFile, string json)> GetMapping(string argZero, List<Container> containers, string dFacesContentDirectory, string d2ResultsFullGroupDirectory, string zPropertyHolderContentDirectory) | ||||
|     { | ||||
|         List<(IFileHolder?, string, FileInfo?, string, string, string)> results = new(); | ||||
|         string key; | ||||
|         string json; | ||||
|         string dateKey; | ||||
|         Mapping? match; | ||||
|         string checkFile; | ||||
|         string directory; | ||||
|         bool? isWrongYear; | ||||
|         string shortcutFile; | ||||
|         FileInfo faceFileInfo; | ||||
|         string? directoryName; | ||||
|         string facesDirectory; | ||||
|         string personDirectory; | ||||
|         List<int> used = new(); | ||||
|         DateTime minimumDateTime; | ||||
|         FileInfo landmarkFileInfo; | ||||
|         string landmarksDirectory; | ||||
|         double deterministicHashCodeKey; | ||||
|         DateTime dateTime = DateTime.Now; | ||||
|         const string facePopulatedKey = nameof(Mapping); | ||||
|         bool deterministicHashCodeKeyValuePairsAny = _DeterministicHashCodeKeyValuePairs.Any(); | ||||
|         foreach (Container container in containers) | ||||
|         { | ||||
|             if (!container.Items.Any()) | ||||
|                 continue; | ||||
|             if (!container.SourceDirectory.StartsWith(argZero)) | ||||
|                 continue; | ||||
|             foreach (Item item in container.Items) | ||||
|             { | ||||
|                 used.Clear(); | ||||
|                 if (item.ImageFileHolder is null || item.Property?.Id is null || item.ResizedFileHolder is null) | ||||
|                     continue; | ||||
|                 directoryName = Path.GetDirectoryName(item.RelativePath); | ||||
|                 if (directoryName is null) | ||||
|                     throw new Exception(); | ||||
|                 foreach (Face face in item.Faces) | ||||
|                 { | ||||
|                     match = null; | ||||
|                     if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) | ||||
|                         continue; | ||||
|                     foreach (Mapping mapping in item.Mapping) | ||||
|                     { | ||||
|                         if (mapping.NormalizedPixelPercentage is null || mapping.NormalizedPixelPercentage.Value != face.Location.NormalizedPixelPercentage.Value) | ||||
|                             continue; | ||||
|                         match = mapping; | ||||
|                         break; | ||||
|                     } | ||||
|                     if (match is null) | ||||
|                         continue; | ||||
|                     foreach (Closest closest in item.Closest) | ||||
|                     { | ||||
|                         if (closest.NormalizedPixelPercentage != face.Location.NormalizedPixelPercentage.Value) | ||||
|                             continue; | ||||
|                         throw new Exception(); | ||||
|                     } | ||||
|                     minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); | ||||
|                     (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); | ||||
|                     dateKey = Stateless.MapLogic.GetDateKey(dateTime, match, minimumDateTime, isWrongYear); | ||||
|                     key = string.Concat(match.PersonKey, dateKey); | ||||
|                     if (match.Filtered is null) | ||||
|                         directory = Path.Combine(zPropertyHolderContentDirectory, $"{facePopulatedKey}Null", match.PersonKey, dateKey); | ||||
|                     else if (!match.Filtered.Value) | ||||
|                         directory = Path.Combine(zPropertyHolderContentDirectory, $"{facePopulatedKey}Okay", match.PersonKey, dateKey); | ||||
|                     else | ||||
|                         directory = Path.Combine(zPropertyHolderContentDirectory, $"{facePopulatedKey}OutOfControl", match.PersonKey, dateKey); | ||||
|                     personDirectory = Path.Combine(directory, match.DisplayDirectoryName[..1], "lnk"); | ||||
|                     results.Add(new(null, personDirectory, null, string.Empty, string.Empty, string.Empty)); | ||||
|                     facesDirectory = string.Concat(dFacesContentDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); | ||||
|                     landmarksDirectory = string.Concat(d2ResultsFullGroupDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); | ||||
|                     deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); | ||||
|                     checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}"); | ||||
|                     faceFileInfo = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.png")); | ||||
|                     landmarkFileInfo = new(Path.Combine(landmarksDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.gif")); | ||||
|                     if (string.IsNullOrEmpty(personDirectory)) | ||||
|                         shortcutFile = string.Empty; | ||||
|                     else | ||||
|                         shortcutFile = Path.Combine(personDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.lnk"); | ||||
|                     results.Add(new(item.ResizedFileHolder, directory, faceFileInfo, checkFile, shortcutFile, string.Empty)); | ||||
|                     if (!string.IsNullOrEmpty(checkFile) && Shared.Models.Stateless.IMapping.SaveFaceEncoding) | ||||
|                     { | ||||
|                         checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.json"); | ||||
|                         json = JsonSerializer.Serialize(face.FaceEncoding); | ||||
|                         results.Add(new(null, directory, null, checkFile, string.Empty, json)); | ||||
|                     } | ||||
|                     personDirectory = Path.Combine(directory, match.DisplayDirectoryName[..1], "lnk", match.DisplayDirectoryName); | ||||
|                     results.Add(new(null, personDirectory, null, string.Empty, string.Empty, string.Empty)); | ||||
|                     used.Add(face.Location.NormalizedPixelPercentage.Value); | ||||
|                 } | ||||
|                 if (deterministicHashCodeKeyValuePairsAny && Shared.Models.Stateless.IMapping.UseDeterministicHashCodeUnknownFaceKeyValuePairsForSaveMapping) | ||||
|                 { | ||||
|                     foreach (Face face in item.Faces) | ||||
|                     { | ||||
|                         match = null; | ||||
|                         if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) | ||||
|                             continue; | ||||
|                         if (named.PersonBirthday is null) | ||||
|                         if (used.Contains(face.Location.NormalizedPixelPercentage.Value)) | ||||
|                             continue; | ||||
|                         if (named.NormalizedPixelPercentage.HasValue && named.NormalizedPixelPercentage.Value != face.Location?.NormalizedPixelPercentage) | ||||
|                             continue; | ||||
|                         key = GetKey(named.MinimumDateTime, named.IsWrongYear, named.PersonBirthday); | ||||
|                         if (!results.ContainsKey(key)) | ||||
|                             results.Add(key, new()); | ||||
|                         results[key].Add(new(named.MinimumDateTime, named.IsWrongYear, named.PersonBirthday, face)); | ||||
|                         if (named.NormalizedPixelPercentage is null) | ||||
|                         // if (item.Faces.Count != 1 || item.Mapping.Count != 1) | ||||
|                         //     continue; | ||||
|                         foreach (Mapping mapping in item.Mapping) | ||||
|                         { | ||||
|                             if (mapping.NormalizedPixelPercentage is not null) | ||||
|                                 continue; | ||||
|                             match = mapping; | ||||
|                             break; | ||||
|                         } | ||||
|                         if (match is null) | ||||
|                             continue; | ||||
|                         foreach (Closest closest in item.Closest) | ||||
|                         { | ||||
|                             if (closest.NormalizedPixelPercentage != face.Location.NormalizedPixelPercentage.Value) | ||||
|                                 continue; | ||||
|                             throw new Exception(); | ||||
|                         } | ||||
|                         minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); | ||||
|                         (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); | ||||
|                         dateKey = Stateless.MapLogic.GetDateKey(dateTime, match, minimumDateTime, isWrongYear); | ||||
|                         if (match.Filtered is null) | ||||
|                             directory = Path.Combine(zPropertyHolderContentDirectory, $"{facePopulatedKey}WithButNull", match.PersonKey, dateKey); | ||||
|                         else if (!match.Filtered.Value) | ||||
|                             directory = Path.Combine(zPropertyHolderContentDirectory, $"{facePopulatedKey}WithAndOkay", match.PersonKey, dateKey); | ||||
|                         else | ||||
|                             directory = Path.Combine(zPropertyHolderContentDirectory, $"{facePopulatedKey}WithButOutOfControl", match.PersonKey, dateKey); | ||||
|                         personDirectory = Path.Combine(directory, match.DisplayDirectoryName[..1], "lnk"); | ||||
|                         results.Add(new(null, personDirectory, null, string.Empty, string.Empty, string.Empty)); | ||||
|                         facesDirectory = string.Concat(dFacesContentDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); | ||||
|                         landmarksDirectory = string.Concat(d2ResultsFullGroupDirectory, Path.Combine(directoryName, item.ImageFileHolder.NameWithoutExtension)); | ||||
|                         deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face); | ||||
|                         checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}"); | ||||
|                         faceFileInfo = new(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.png")); | ||||
|                         landmarkFileInfo = new(Path.Combine(landmarksDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.gif")); | ||||
|                         if (string.IsNullOrEmpty(personDirectory)) | ||||
|                             shortcutFile = string.Empty; | ||||
|                         else | ||||
|                             shortcutFile = Path.Combine(personDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.lnk"); | ||||
|                         results.Add(new(item.ResizedFileHolder, directory, faceFileInfo, checkFile, shortcutFile, string.Empty)); | ||||
|                         if (!string.IsNullOrEmpty(checkFile) && Shared.Models.Stateless.IMapping.SaveFaceEncoding) | ||||
|                         { | ||||
|                             checkFile = Path.Combine(directory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}.json"); | ||||
|                             json = JsonSerializer.Serialize(face.FaceEncoding); | ||||
|                             results.Add(new(null, directory, null, checkFile, string.Empty, json)); | ||||
|                         } | ||||
|                         personDirectory = Path.Combine(directory, match.DisplayDirectoryName[..1], "lnk", match.DisplayDirectoryName); | ||||
|                         results.Add(new(null, personDirectory, null, string.Empty, string.Empty, string.Empty)); | ||||
|                         used.Add(face.Location.NormalizedPixelPercentage.Value); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @ -529,14 +763,63 @@ public class MapLogic | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     public static (bool?, string[]) IsWrongYear(Shared.Models.Item item) | ||||
|     private static void Save(List<(IFileHolder? resizedFileHolder, string directory, FileInfo? faceFileInfo, string checkFile, string shortcutFile, string json)> collection) | ||||
|     { | ||||
|         (bool?, string[]) result; | ||||
|         if (item.Property is null || item.ImageFileHolder is null) | ||||
|             throw new NullReferenceException(); | ||||
|         DateTime? minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); | ||||
|         result = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); | ||||
|         return result; | ||||
|         WindowsShortcut windowsShortcut; | ||||
|         string[] directories = (from l in collection select l.directory).Distinct().ToArray(); | ||||
|         foreach (string directory in directories) | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(directory)) | ||||
|                 continue; | ||||
|             if (!Directory.Exists(directory)) | ||||
|                 _ = Directory.CreateDirectory(directory); | ||||
|         } | ||||
|         foreach ((IFileHolder? resizedFileHolder, string directory, FileInfo? faceFileInfo, string checkFile, string shortcutFile, string json) in collection) | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(directory) || string.IsNullOrEmpty(checkFile) || resizedFileHolder is null || faceFileInfo is null || !string.IsNullOrEmpty(json)) | ||||
|                 continue; | ||||
|             if (File.Exists(checkFile)) | ||||
|                 continue; | ||||
|             if (faceFileInfo.Directory is not null && faceFileInfo.Directory.Exists && faceFileInfo.Exists) | ||||
|                 File.Copy(faceFileInfo.FullName, checkFile); | ||||
|             else | ||||
|                 File.Copy(resizedFileHolder.FullName, checkFile); | ||||
|         } | ||||
|         foreach ((IFileHolder? resizedFileHolder, string directory, FileInfo? faceFileInfo, string checkFile, string shortcutFile, string json) in collection) | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(directory) || string.IsNullOrEmpty(checkFile) || resizedFileHolder is not null || faceFileInfo is not null || string.IsNullOrEmpty(json)) | ||||
|                 continue; | ||||
|             if (File.Exists(checkFile)) | ||||
|                 continue; | ||||
|             _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(checkFile, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); | ||||
|         } | ||||
|         foreach ((IFileHolder? resizedFileHolder, string directory, FileInfo? _, string checkFile, string shortcutFile, string json) in collection) | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(directory) || string.IsNullOrEmpty(checkFile) || resizedFileHolder is null) | ||||
|                 continue; | ||||
|             if (string.IsNullOrEmpty(shortcutFile) || !resizedFileHolder.Exists) | ||||
|                 continue; | ||||
|             try | ||||
|             { | ||||
|                 windowsShortcut = new() { Path = resizedFileHolder.FullName }; | ||||
|                 windowsShortcut.Save(shortcutFile); | ||||
|                 windowsShortcut.Dispose(); | ||||
|             } | ||||
|             catch (Exception) | ||||
|             { } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void SaveClosest(string argZero, List<Container> containers, string dFacesContentDirectory, string d2ResultsFullGroupDirectory, string zPropertyHolderContentDirectory) | ||||
|     { | ||||
|         List<(IFileHolder? resizedFileHolder, string directory, FileInfo? faceFileInfo, string checkFile, string shortcutFile, string json)> collection = GetClosest(argZero, containers, dFacesContentDirectory, d2ResultsFullGroupDirectory, zPropertyHolderContentDirectory); | ||||
|         Save(collection); | ||||
|     } | ||||
|  | ||||
|     public void SaveMapping(string argZero, List<Container> containers, string dFacesContentDirectory, string d2ResultsFullGroupDirectory, string zPropertyHolderContentDirectory) | ||||
|     { | ||||
|         List<(IFileHolder? resizedFileHolder, string directory, FileInfo? faceFileInfo, string checkFile, string shortcutFile, string json)> collection = GetMapping(argZero, containers, dFacesContentDirectory, d2ResultsFullGroupDirectory, zPropertyHolderContentDirectory); | ||||
|         Save(collection); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										18
									
								
								Map/Models/Stateless/IMapLogic.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Map/Models/Stateless/IMapLogic.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| namespace View_by_Distance.Map.Models.Stateless; | ||||
|  | ||||
| public interface IMapLogic | ||||
| { // ... | ||||
|  | ||||
|     (bool?, string[]) TestStatic_IsWrongYear(Shared.Models.Item item); | ||||
|     static (bool?, string[]) IsWrongYear(Shared.Models.Item item) => | ||||
|        MapLogic.IsWrongYear(item); | ||||
|  | ||||
|     string TestStatic_GetDateKey(DateTime dateTime, Shared.Models.Mapping mapping, DateTime minimumDateTime, bool? isWrongYear); | ||||
|     static string GetDateKey(DateTime dateTime, Shared.Models.Mapping mapping, DateTime minimumDateTime, bool? isWrongYear) => | ||||
|        MapLogic.GetDateKey(dateTime, mapping, minimumDateTime, isWrongYear); | ||||
|  | ||||
|     Dictionary<string, List<Shared.Models.MappingContainer>> TestStatic_GetKeyValuePairs(string[] ignoreRelativePaths, string argZero, List<Shared.Models.Container> containers); | ||||
|     static Dictionary<string, List<Shared.Models.MappingContainer>> GetKeyValuePairs(string[] ignoreRelativePaths, string argZero, List<Shared.Models.Container> containers) => | ||||
|        MapLogic.GetKeyValuePairs(ignoreRelativePaths, argZero, containers); | ||||
|  | ||||
| } | ||||
							
								
								
									
										93
									
								
								Map/Models/Stateless/MapLogic.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								Map/Models/Stateless/MapLogic.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | ||||
| using View_by_Distance.Shared.Models; | ||||
|  | ||||
| namespace View_by_Distance.Map.Models.Stateless; | ||||
|  | ||||
| internal abstract class MapLogic | ||||
| { | ||||
|  | ||||
|     internal static string GetDateKey(DateTime dateTime, Mapping mapping, DateTime minimumDateTime, bool? isWrongYear) | ||||
|     { | ||||
|         int years; | ||||
|         string result; | ||||
|         TimeSpan? timeSpan = Shared.Models.Stateless.Methods.IPersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear, mapping.PersonBirthday); | ||||
|         if (timeSpan.HasValue && timeSpan.Value.Ticks < 0) | ||||
|             result = "!---"; | ||||
|         else if (timeSpan.HasValue) | ||||
|         { | ||||
|             (years, _) = Shared.Models.Stateless.Methods.IPersonBirthday.GetAge(minimumDateTime, mapping.PersonBirthday); | ||||
|             result = $"^{years:000}"; | ||||
|         } | ||||
|         else if (mapping.ApproximateYears.HasValue) | ||||
|         { | ||||
|             (years, _) = Shared.Models.Stateless.Methods.IAge.GetAge(minimumDateTime, dateTime.AddYears(-mapping.ApproximateYears.Value)); | ||||
|             result = $"~{years:000}"; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             string isWrongYearFlag = Shared.Models.Stateless.Methods.IItem.GetWrongYearFlag(isWrongYear); | ||||
|             result = $"{isWrongYearFlag}{minimumDateTime:yyyy}"; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     internal static Dictionary<string, List<MappingContainer>> GetKeyValuePairs(string[] ignoreRelativePaths, string argZero, List<Container> containers) | ||||
|     { | ||||
|         Dictionary<string, List<MappingContainer>> results = new(); | ||||
|         string key; | ||||
|         string dateKey; | ||||
|         bool? isWrongYear; | ||||
|         DateTime minimumDateTime; | ||||
|         DateTime dateTime = DateTime.Now; | ||||
|         MappingContainer mappingContainer; | ||||
|         foreach (Container container in containers) | ||||
|         { | ||||
|             if (!container.Items.Any()) | ||||
|                 continue; | ||||
|             if (!container.SourceDirectory.StartsWith(argZero)) | ||||
|                 continue; | ||||
|             if (ignoreRelativePaths.Contains(Path.GetFileName(container.SourceDirectory))) | ||||
|                 continue; | ||||
|             foreach (Item item in container.Items) | ||||
|             { | ||||
|                 if (item.ImageFileHolder is null || item.Property?.Id is null || !item.Mapping.Any()) | ||||
|                     continue; | ||||
|                 foreach (Face face in item.Faces) | ||||
|                 { | ||||
|                     if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) | ||||
|                         continue; | ||||
|                     foreach (Mapping mapping in item.Mapping) | ||||
|                     { | ||||
|                         if (mapping.PersonBirthday is null) | ||||
|                             continue; | ||||
|                         if (mapping.NormalizedPixelPercentage.HasValue && mapping.NormalizedPixelPercentage.Value != face.Location.NormalizedPixelPercentage.Value) | ||||
|                             continue; | ||||
|                         // if (named.NormalizedPixelPercentage is null && (Shared.Models.Stateless.INamed.OnlyUseNamedWithNormalizedPixelPercentagePopulatedForGetKeyValuePairs || item.Named.Count != 1 || item.Faces.Count != 1)) | ||||
|                         //     continue; | ||||
|                         minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); | ||||
|                         (isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); | ||||
|                         dateKey = GetDateKey(dateTime, mapping, minimumDateTime, isWrongYear); | ||||
|                         key = string.Concat(mapping.PersonKey, dateKey); | ||||
|                         if (!results.ContainsKey(key)) | ||||
|                             results.Add(key, new()); | ||||
|                         mappingContainer = new(face, item.Property.Id.Value, isWrongYear, key, mapping, minimumDateTime); | ||||
|                         results[key].Add(mappingContainer); | ||||
|                         // if (named.NormalizedPixelPercentage is null) | ||||
|                         //     break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|     internal static (bool?, string[]) IsWrongYear(Item item) | ||||
|     { | ||||
|         (bool?, string[]) result; | ||||
|         if (item.Property is null || item.ImageFileHolder is null) | ||||
|             throw new NullReferenceException(); | ||||
|         DateTime? minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); | ||||
|         result = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										220
									
								
								Map/Models/Stateless/SetByDeterministicHashCode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								Map/Models/Stateless/SetByDeterministicHashCode.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,220 @@ | ||||
| using System.Globalization; | ||||
| using System.Text.Json; | ||||
| using View_by_Distance.Shared.Models; | ||||
|  | ||||
| namespace View_by_Distance.Map.Models.Stateless; | ||||
|  | ||||
| public class ByDeterministicHashCode | ||||
| { | ||||
|  | ||||
|     private static void SetOther(string outputExtension, Dictionary<string, Person> personKeyValuePairs, string deterministicHashCodePeopleDirectory, List<double> skipCollection, List<(string, int?, string, PersonBirthday[])> peopleCollection) | ||||
|     { | ||||
|         string json; | ||||
|         string personKey; | ||||
|         string[] segments; | ||||
|         int? approximateYears; | ||||
|         string groupDirectoryName; | ||||
|         string personKeyJsonFileName; | ||||
|         string[] personKeyDirectories; | ||||
|         string personKeyJsonDirectory; | ||||
|         PersonBirthday? personBirthday; | ||||
|         string[] personDisplayDirectories; | ||||
|         string convertedPersonKeyDirectory; | ||||
|         string? personDisplayDirectoryName; | ||||
|         List<PersonBirthday> personBirthdays; | ||||
|         string[] groupDirectories = Directory.GetDirectories(deterministicHashCodePeopleDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|         foreach (string groupDirectory in groupDirectories) | ||||
|         { | ||||
|             groupDirectoryName = Path.GetFileName(groupDirectory); | ||||
|             if (groupDirectoryName[0] == '!') | ||||
|             { | ||||
|                 skipCollection.AddRange(from l in Directory.GetFiles(groupDirectory, $"*{outputExtension}", SearchOption.AllDirectories) select double.Parse(Path.GetFileNameWithoutExtension(l))); | ||||
|                 continue; | ||||
|             } | ||||
|             else if (groupDirectoryName[0] is not '_' and not '~' and not '^') | ||||
|                 continue; | ||||
|             skipCollection.AddRange(from l in Directory.GetFiles(groupDirectory, $"*{outputExtension}", SearchOption.AllDirectories) select double.Parse(Path.GetFileNameWithoutExtension(l))); | ||||
|             personDisplayDirectories = Directory.GetDirectories(groupDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|             foreach (string personDisplayDirectory in personDisplayDirectories) | ||||
|             { | ||||
|                 personBirthdays = new(); | ||||
|                 personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory); | ||||
|                 if (string.IsNullOrEmpty(personDisplayDirectoryName)) | ||||
|                     continue; | ||||
|                 if (groupDirectoryName[0] != '~') | ||||
|                     approximateYears = null; | ||||
|                 else | ||||
|                 { | ||||
|                     segments = personDisplayDirectoryName.Split('~'); | ||||
|                     if (segments.Length == 1 || !int.TryParse(segments[1].Split('-')[0], out int years)) | ||||
|                         approximateYears = null; | ||||
|                     else | ||||
|                         approximateYears = years; | ||||
|                 } | ||||
|                 personKeyDirectories = Directory.GetDirectories(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                 foreach (string personKeyDirectory in personKeyDirectories) | ||||
|                 { | ||||
|                     personKey = Path.GetFileName(personKeyDirectory); | ||||
|                     if (!DateTime.TryParseExact(personKey, "MM.dd.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime birthday)) | ||||
|                         personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKey); | ||||
|                     else | ||||
|                     { | ||||
|                         personBirthday = new PersonBirthday(birthday); | ||||
|                         personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(personBirthday); | ||||
|                         convertedPersonKeyDirectory = Path.Combine(personDisplayDirectory, personKey); | ||||
|                         if (!Directory.Exists(convertedPersonKeyDirectory)) | ||||
|                             Directory.Move(personKeyDirectory, convertedPersonKeyDirectory); | ||||
|                     } | ||||
|                     if (personBirthday is null) | ||||
|                         continue; | ||||
|                     personBirthdays.Add(personBirthday); | ||||
|                 } | ||||
|                 foreach (string personKeyDirectory in personKeyDirectories) | ||||
|                 { | ||||
|                     personKey = Path.GetFileName(personKeyDirectory); | ||||
|                     personBirthday = Shared.Models.Stateless.Methods.IPersonBirthday.GetPersonBirthday(personKey); | ||||
|                     if (personBirthday is null) | ||||
|                         continue; | ||||
|                     if (personKeyValuePairs.ContainsKey(personKey)) | ||||
|                     { | ||||
|                         personKeyJsonDirectory = Path.Combine(personDisplayDirectory, personKey); | ||||
|                         if (!Directory.Exists(personKeyJsonDirectory)) | ||||
|                             Directory.Move(personKeyDirectory, personKeyJsonDirectory); | ||||
|                         personKeyJsonFileName = Path.Combine(personKeyJsonDirectory, $"{personKey}.json"); | ||||
|                         json = JsonSerializer.Serialize(personKeyValuePairs[personKey], new JsonSerializerOptions() { WriteIndented = true }); | ||||
|                         _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(personKeyJsonFileName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); | ||||
|                     } | ||||
|                     peopleCollection.Add(new(personDisplayDirectoryName, approximateYears, personKey, personBirthdays.OrderByDescending(l => l.Value).ToArray())); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal static void SetKeyValuePairs(string deterministicHashCodeContentDirectory, List<(string, double)> deterministicHashCodeCollection, List<(string, double)> incorrectDeterministicHashCodeCollection, Dictionary<int, List<Face>> keyValuePairs) | ||||
|     { | ||||
|         string[] files; | ||||
|         string personKey; | ||||
|         string[] yearDirectories; | ||||
|         string ticksDirectoryName; | ||||
|         string? personFirstInitial; | ||||
|         string[] personKeyDirectories; | ||||
|         string[] personNameDirectories; | ||||
|         string[] personNameLinkDirectories; | ||||
|         string? personFirstInitialDirectory; | ||||
|         double? reversedDeterministicHashCodeKey; | ||||
|         bool keyValuePairsAny = keyValuePairs.Any(); | ||||
|         string[] ticksDirectories = Directory.GetDirectories(deterministicHashCodeContentDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|         foreach (string ticksDirectory in ticksDirectories) | ||||
|         { | ||||
|             ticksDirectoryName = Path.GetFileName(ticksDirectory); | ||||
|             if (ticksDirectoryName.Length < 3 || ticksDirectoryName[0] != '(' || ticksDirectoryName[^1] != ')') | ||||
|                 continue; | ||||
|             personKeyDirectories = Directory.GetDirectories(ticksDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|             foreach (string personKeyDirectory in personKeyDirectories) | ||||
|             { | ||||
|                 personKey = Path.GetFileName(personKeyDirectory); | ||||
|                 if (personKey == nameof(Closest)) | ||||
|                     throw new Exception($"Move personKey directories up one from {nameof(Closest)} and delete {nameof(Closest)} directory!"); | ||||
|                 yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                 foreach (string yearDirectory in yearDirectories) | ||||
|                 { | ||||
|                     files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                     personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                     foreach (string file in files) | ||||
|                         File.Delete(file); | ||||
|                     foreach (string personNameDirectory in personNameDirectories) | ||||
|                     { | ||||
|                         personFirstInitial = Path.GetFileName(personNameDirectory)[..1]; | ||||
|                         if (personFirstInitial is null) | ||||
|                             continue; | ||||
|                         personFirstInitialDirectory = Path.Combine(yearDirectory, personFirstInitial); | ||||
|                         files = Directory.GetFiles(personNameDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                         foreach (string file in files) | ||||
|                         { | ||||
|                             if (file.EndsWith(".lnk") || file.EndsWith(".json")) | ||||
|                                 continue; | ||||
|                             reversedDeterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetReversedDeterministicHashCodeKey(keyValuePairsAny, keyValuePairs, file); | ||||
|                             if (reversedDeterministicHashCodeKey is null) | ||||
|                                 continue; | ||||
|                             deterministicHashCodeCollection.Add(new(personKey, reversedDeterministicHashCodeKey.Value)); | ||||
|                         } | ||||
|                         if (personNameDirectory == personFirstInitialDirectory) | ||||
|                             continue; | ||||
|                         personNameLinkDirectories = Directory.GetDirectories(personNameDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                         foreach (string personNameLinkDirectory in personNameLinkDirectories) | ||||
|                         { | ||||
|                             files = Directory.GetFiles(personNameLinkDirectory, "*", SearchOption.TopDirectoryOnly); | ||||
|                             foreach (string file in files) | ||||
|                             { | ||||
|                                 if (!file.EndsWith(".lnk")) | ||||
|                                     continue; | ||||
|                                 File.Delete(file); | ||||
|                             } | ||||
|                             _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(personNameLinkDirectory); | ||||
|                         } | ||||
|                         Directory.Move(personNameDirectory, personFirstInitialDirectory); | ||||
|                         _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(personNameDirectory); | ||||
|                     } | ||||
|                     _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(yearDirectory); | ||||
|                 } | ||||
|                 _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(personKeyDirectory); | ||||
|             } | ||||
|             _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(ticksDirectory); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal static string SetByRef(string outputExtension, Dictionary<string, Person> personKeyValuePairs, List<double> skipCollection, Dictionary<string, (string, int?, string, PersonBirthday[])> peopleKeyValuePairs, Dictionary<int, string[]> deterministicHashCodeUnknownFaceKeyValuePairs, Dictionary<double, string[]> deterministicHashCodeKeyValuePairs, Dictionary<double, string[]> incorrectDeterministicHashCodeKeyValuePairs, string deterministicHashCodeRootDirectory) | ||||
|     { | ||||
|         string result; | ||||
|         List<string> deterministicHashCodePersonKeys = new(); | ||||
|         List<string> deterministicHashCodeUnknownFacePersonKeys = new(); | ||||
|         foreach (KeyValuePair<int, string[]> keyValuePair in deterministicHashCodeUnknownFaceKeyValuePairs) | ||||
|             deterministicHashCodeUnknownFacePersonKeys.AddRange(keyValuePair.Value); | ||||
|         deterministicHashCodeUnknownFacePersonKeys = deterministicHashCodeUnknownFacePersonKeys.Distinct().ToList(); | ||||
|         List<(string, int?, string, PersonBirthday[])> peopleCollection = new(); | ||||
|         string deterministicHashCodePeopleDirectory = Path.Combine(deterministicHashCodeRootDirectory, "People"); | ||||
|         if (Directory.Exists(deterministicHashCodePeopleDirectory)) | ||||
|             SetOther(outputExtension, personKeyValuePairs, deterministicHashCodePeopleDirectory, skipCollection, peopleCollection); | ||||
|         result = Path.Combine(deterministicHashCodeRootDirectory, "()"); | ||||
|         if (!Directory.Exists(result)) | ||||
|             result = string.Empty; | ||||
|         else | ||||
|         { | ||||
|             Dictionary<int, List<Face>> keyValuePairs = new(); | ||||
|             Dictionary<double, List<string>> deterministicHashCodeScope = new(); | ||||
|             Dictionary<double, List<string>> incorrectDeterministicHashCodeScope = new(); | ||||
|             List<(string PersonKey, double IdAndNormalizedPixelPercentage)> deterministicHashCodeCollection = new(); | ||||
|             List<(string PersonKey, double IdAndNormalizedPixelPercentage)> incorrectDeterministicHashCodeCollection = new(); | ||||
|             SetKeyValuePairs(result, deterministicHashCodeCollection, incorrectDeterministicHashCodeCollection, keyValuePairs); | ||||
|             deterministicHashCodeCollection = (from l in deterministicHashCodeCollection orderby l.IdAndNormalizedPixelPercentage select l).ToList(); | ||||
|             incorrectDeterministicHashCodeCollection = (from l in incorrectDeterministicHashCodeCollection orderby l.IdAndNormalizedPixelPercentage select l).ToList(); | ||||
|             foreach ((string personKey, double idAndNormalizedPixelPercentage) in deterministicHashCodeCollection) | ||||
|             { | ||||
|                 if (!deterministicHashCodeScope.ContainsKey(idAndNormalizedPixelPercentage)) | ||||
|                     deterministicHashCodeScope.Add(idAndNormalizedPixelPercentage, new()); | ||||
|                 deterministicHashCodeScope[idAndNormalizedPixelPercentage].Add(personKey); | ||||
|                 deterministicHashCodePersonKeys.Add(personKey); | ||||
|             } | ||||
|             deterministicHashCodePersonKeys = deterministicHashCodePersonKeys.Distinct().ToList(); | ||||
|             foreach ((string personKey, double idAndNormalizedPixelPercentage) in incorrectDeterministicHashCodeCollection) | ||||
|             { | ||||
|                 if (!incorrectDeterministicHashCodeScope.ContainsKey(idAndNormalizedPixelPercentage)) | ||||
|                     incorrectDeterministicHashCodeScope.Add(idAndNormalizedPixelPercentage, new()); | ||||
|                 incorrectDeterministicHashCodeScope[idAndNormalizedPixelPercentage].Add(personKey); | ||||
|             } | ||||
|             foreach (KeyValuePair<double, List<string>> keyValuePair in deterministicHashCodeScope) | ||||
|                 deterministicHashCodeKeyValuePairs.Add(keyValuePair.Key, keyValuePair.Value.Distinct().ToArray()); | ||||
|             foreach (KeyValuePair<double, List<string>> keyValuePair in incorrectDeterministicHashCodeScope) | ||||
|                 incorrectDeterministicHashCodeKeyValuePairs.Add(keyValuePair.Key, keyValuePair.Value.Distinct().ToArray()); | ||||
|         } | ||||
|         foreach ((string personDisplayDirectoryName, int? approximateYears, string personKey, PersonBirthday[] personBirthdays) in peopleCollection) | ||||
|         { | ||||
|             if (peopleKeyValuePairs.ContainsKey(personKey) && peopleKeyValuePairs[personKey].Item1 != personDisplayDirectoryName) | ||||
|                 throw new NotImplementedException(); | ||||
|             if (deterministicHashCodeUnknownFacePersonKeys.Contains(personKey) || deterministicHashCodePersonKeys.Contains(personKey)) | ||||
|                 peopleKeyValuePairs.Add(personKey, new(personDisplayDirectoryName, approximateYears, personKey, personBirthdays)); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -295,7 +295,7 @@ public class PropertyCompareLogic | ||||
|                 continue; | ||||
|             totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); | ||||
|             using ProgressBar progressBar = new(filteredSourceDirectoryFiles.Length, $"{r + 1:000}.{g} / {groupCollection.Count:000}) {filteredSourceDirectoryFiles.Length:000} file(s) - {totalSeconds} total second(s) - {sourceDirectory}", options); | ||||
|             _ = Parallel.For(0, filteredSourceDirectoryFiles.Length, parallelOptions, i => | ||||
|             _ = Parallel.For(0, filteredSourceDirectoryFiles.Length, parallelOptions, (i, state) => | ||||
|                { | ||||
|                    try | ||||
|                    { | ||||
|  | ||||
| @ -571,7 +571,7 @@ public class A_Property | ||||
|         ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; | ||||
|         string message = $"{container.R:000}.{container.G} / {containersCount:000}) {filteredItems.Length:000} file(s) - {totalSeconds} total second(s) - {container.SourceDirectory}"; | ||||
|         using ProgressBar progressBar = new(filteredItems.Length, message, options); | ||||
|         _ = Parallel.For(0, filteredItems.Length, parallelOptions, i => | ||||
|         _ = Parallel.For(0, filteredItems.Length, parallelOptions, (i, state) => | ||||
|            { | ||||
|                try | ||||
|                { | ||||
|  | ||||
| @ -140,7 +140,7 @@ public class Container | ||||
|         List<(int, string, List<(string, Shared.Models.Property?)>, int)> results = new(); | ||||
|         int length = rootDirectory.Length; | ||||
|         ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = Environment.ProcessorCount }; | ||||
|         _ = Parallel.For(0, jsonCollection.Count, parallelOptions, i => ParallelFor(jsonCollection, i, length, results)); | ||||
|         _ = Parallel.For(0, jsonCollection.Count, parallelOptions, (i, state) => ParallelFor(jsonCollection, i, length, results)); | ||||
|         return results; | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -6,38 +6,33 @@ namespace View_by_Distance.Shared.Models; | ||||
| public class Closest : Properties.IClosest | ||||
| { | ||||
|  | ||||
|     protected readonly double? _Average; | ||||
|     protected readonly int? _NormalizedPixelPercentage; | ||||
|     protected readonly int _Average; | ||||
|     protected readonly bool? _IsWrongYear; | ||||
|     protected readonly double? _Minimum; | ||||
|     protected Mapping _Mapping; | ||||
|     protected readonly double _Minimum; | ||||
|     protected readonly DateTime _MinimumDateTime; | ||||
|     protected readonly PersonBirthday? _PersonBirthday; | ||||
|     public double? Average => _Average; | ||||
|     public int? NormalizedPixelPercentage => _NormalizedPixelPercentage; | ||||
|     protected readonly int _NormalizedPixelPercentage; | ||||
|     protected readonly long? _TicksDelta; | ||||
|     public double Average => _Average; | ||||
|     public bool? IsWrongYear => _IsWrongYear; | ||||
|     public double? Minimum => _Minimum; | ||||
|     public Mapping Mapping => _Mapping; | ||||
|     public double Minimum => _Minimum; | ||||
|     public DateTime MinimumDateTime => _MinimumDateTime; | ||||
|     public PersonBirthday? PersonBirthday => _PersonBirthday; | ||||
|     public int NormalizedPixelPercentage => _NormalizedPixelPercentage; | ||||
|     public long? TicksDelta => _TicksDelta; | ||||
|  | ||||
|     [JsonConstructor] | ||||
|     public Closest(double? average, int? normalizedPixelPercentage, bool? isWrongYear, double? minimum, DateTime minimumDateTime, PersonBirthday? personBirthday) | ||||
|     public Closest(int average, int normalizedPixelPercentage, bool? isWrongYear, Mapping mapping, double minimum, DateTime minimumDateTime, long? ticksDelta) | ||||
|     { | ||||
|         _Average = average; | ||||
|         _NormalizedPixelPercentage = normalizedPixelPercentage; | ||||
|         _IsWrongYear = isWrongYear; | ||||
|         _Mapping = mapping; | ||||
|         _Minimum = minimum; | ||||
|         _MinimumDateTime = minimumDateTime; | ||||
|         _PersonBirthday = personBirthday; | ||||
|         _TicksDelta = ticksDelta; | ||||
|     } | ||||
|  | ||||
|     public Closest(int? normalizedPixelPercentage, DateTime minimumDateTime, bool? isWrongYear) : | ||||
|         this(null, normalizedPixelPercentage, isWrongYear, null, minimumDateTime, null) | ||||
|     { } | ||||
|  | ||||
|     public Closest(int? normalizedPixelPercentage, DateTime minimumDateTime, bool? isWrongYear, PersonBirthday? personBirthday, List<double> faceDistances) : | ||||
|         this(faceDistances.Average(), normalizedPixelPercentage, isWrongYear, faceDistances.Min(), minimumDateTime, personBirthday) | ||||
|     { } | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); | ||||
|  | ||||
| @ -7,6 +7,7 @@ public class Face : Properties.IFace | ||||
| { | ||||
|  | ||||
|     protected DateTime _DateTime; | ||||
|     protected List<FaceDistance> _FaceDistances; | ||||
|     protected FaceEncoding? _FaceEncoding; | ||||
|     protected Dictionary<Stateless.FacePart, FacePoint[]>? _FaceParts; | ||||
|     protected readonly OutputResolution? _OutputResolution; | ||||
| @ -14,6 +15,7 @@ public class Face : Properties.IFace | ||||
|     protected readonly int? _LocationIndex; | ||||
|     protected readonly string _RelativePath; | ||||
|     public DateTime DateTime => _DateTime; | ||||
|     public List<FaceDistance> FaceDistances => _FaceDistances; | ||||
|     public FaceEncoding? FaceEncoding => _FaceEncoding; | ||||
|     public Dictionary<Stateless.FacePart, FacePoint[]>? FaceParts => _FaceParts; | ||||
|     public Location? Location => _Location; | ||||
| @ -22,9 +24,12 @@ public class Face : Properties.IFace | ||||
|     public string RelativePath => _RelativePath; | ||||
|  | ||||
|     [JsonConstructor] | ||||
|     public Face(DateTime dateTime, FaceEncoding? faceEncoding, Dictionary<Stateless.FacePart, FacePoint[]>? faceParts, Location? location, int? locationIndex, OutputResolution? outputResolution, string relativePath) | ||||
|     public Face(DateTime dateTime, List<FaceDistance> faceDistances, FaceEncoding? faceEncoding, Dictionary<Stateless.FacePart, FacePoint[]>? faceParts, Location? location, int? locationIndex, OutputResolution? outputResolution, string relativePath) | ||||
|     { | ||||
|         if (faceDistances is null) | ||||
|             faceDistances = new(); | ||||
|         _DateTime = dateTime; | ||||
|         _FaceDistances = faceDistances; | ||||
|         _FaceEncoding = faceEncoding; | ||||
|         _FaceParts = faceParts; | ||||
|         _Location = location; | ||||
| @ -34,29 +39,29 @@ public class Face : Properties.IFace | ||||
|     } | ||||
|  | ||||
|     public Face() : | ||||
|         this(DateTime.MinValue, null, null, null, null, null, string.Empty) | ||||
|         this(DateTime.MinValue, new(), null, null, null, null, null, string.Empty) | ||||
|     { } | ||||
|  | ||||
|     public Face(Location location) : | ||||
|         this(DateTime.MinValue, null, null, location, null, null, string.Empty) | ||||
|         this(DateTime.MinValue, new(), null, null, location, null, null, string.Empty) | ||||
|     { } | ||||
|  | ||||
|     public Face(int facesCount, Face face) : | ||||
|         this(face.DateTime, face.FaceEncoding, face.FaceParts, face.Location, face.LocationIndex, face.OutputResolution, face.RelativePath) | ||||
|         this(face.DateTime, new(), face.FaceEncoding, face.FaceParts, face.Location, face.LocationIndex, face.OutputResolution, face.RelativePath) | ||||
|     { | ||||
|         if (face.Location?.Confidence is not null && face.OutputResolution is not null) | ||||
|             _Location = new(face.Location.Confidence, face.OutputResolution.Height, face.Location, face.OutputResolution.Width, facesCount); | ||||
|     } | ||||
|  | ||||
|     public Face(int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, Face face) : | ||||
|         this(face.DateTime, face.FaceEncoding, face.FaceParts, face.Location, face.LocationIndex, null, face.RelativePath) | ||||
|         this(face.DateTime, new(), face.FaceEncoding, face.FaceParts, face.Location, face.LocationIndex, null, face.RelativePath) | ||||
|     { | ||||
|         if (outputResolutionHeight > 0) | ||||
|             _OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth); | ||||
|     } | ||||
|  | ||||
|     public Face(Property property, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, string relativePath, int? i, Location? location) : | ||||
|         this(DateTime.MinValue, null, null, location, i, null, relativePath) | ||||
|         this(DateTime.MinValue, new(), null, null, location, i, null, relativePath) | ||||
|     { | ||||
|         DateTime?[] dateTimes; | ||||
|         _OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth); | ||||
|  | ||||
							
								
								
									
										36
									
								
								Shared/Models/FaceDistance.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								Shared/Models/FaceDistance.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace View_by_Distance.Shared.Models; | ||||
|  | ||||
| public class FaceDistance : Properties.IFaceDistance | ||||
| { | ||||
|  | ||||
|     protected readonly List<double> _Distances; | ||||
|     protected readonly bool? _IsWrongYear; | ||||
|     protected readonly string _Key; | ||||
|     protected readonly Mapping _Mapping; | ||||
|     protected readonly DateTime _MinimumDateTime; | ||||
|     public List<double> Distances => _Distances; | ||||
|     public bool? IsWrongYear => _IsWrongYear; | ||||
|     public string Key => _Key; | ||||
|     public Mapping Mapping => _Mapping; | ||||
|     public DateTime MinimumDateTime => _MinimumDateTime; | ||||
|  | ||||
|     [JsonConstructor] | ||||
|     public FaceDistance(List<double> distances, bool? isWrongYear, string key, Mapping mapping, DateTime minimumDateTime) | ||||
|     { | ||||
|         _Distances = distances; | ||||
|         _IsWrongYear = isWrongYear; | ||||
|         _Key = key; | ||||
|         _Mapping = mapping; | ||||
|         _MinimumDateTime = minimumDateTime; | ||||
|     } | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -11,8 +11,8 @@ public class Item : Properties.IItem | ||||
|     protected List<Closest> _Closest; | ||||
|     protected List<Face> _Faces; | ||||
|     protected readonly FileHolder? _ImageFileHolder; | ||||
|     protected List<Mapping> _Mapping; | ||||
|     protected bool? _Moved; | ||||
|     protected List<Named> _Named; | ||||
|     protected readonly bool? _NoJson; | ||||
|     protected Property? _Property; | ||||
|     protected readonly string _RelativePath; | ||||
| @ -24,9 +24,9 @@ public class Item : Properties.IItem | ||||
|     public List<Closest> Closest => _Closest; | ||||
|     public List<Face> Faces => _Faces; | ||||
|     public FileHolder? ImageFileHolder => _ImageFileHolder; | ||||
|     public List<Mapping> Mapping => _Mapping; | ||||
|     public bool? Moved => _Moved; | ||||
|     public bool? NoJson => _NoJson; | ||||
|     public List<Named> Named => _Named; | ||||
|     public Property? Property => _Property; | ||||
|     public string RelativePath => _RelativePath; | ||||
|     public FileHolder? ResizedFileHolder => _ResizedFileHolder; | ||||
| @ -34,15 +34,15 @@ public class Item : Properties.IItem | ||||
|     public bool ValidImageFormatExtension => _ValidImageFormatExtension; | ||||
|  | ||||
|     [JsonConstructor] | ||||
|     public Item(bool? abandoned, bool? changed, List<Closest> closest, List<Face> faces, FileHolder? imageFileHolder, bool? moved, List<Named> named, bool? noJson, Property? property, string relativePath, FileHolder? resizedFileHolder, string sourceDirectoryFile, bool validImageFormatExtension) | ||||
|     public Item(bool? abandoned, bool? changed, List<Closest> closest, List<Face> faces, FileHolder? imageFileHolder, List<Mapping> mapping, bool? moved, bool? noJson, Property? property, string relativePath, FileHolder? resizedFileHolder, string sourceDirectoryFile, bool validImageFormatExtension) | ||||
|     { | ||||
|         _Abandoned = abandoned; | ||||
|         _Changed = changed; | ||||
|         _Closest = closest; | ||||
|         _Faces = faces; | ||||
|         _ImageFileHolder = imageFileHolder; | ||||
|         _Mapping = mapping; | ||||
|         _Moved = moved; | ||||
|         _Named = named; | ||||
|         _NoJson = noJson; | ||||
|         _Property = property; | ||||
|         _RelativePath = relativePath; | ||||
| @ -54,7 +54,7 @@ public class Item : Properties.IItem | ||||
|     public Item(string sourceDirectoryFile, string relativePath, FileHolder? imageFileInfo, bool isValidImageFormatExtension, Property? property, bool? abandoned, bool? changed) | ||||
|     { | ||||
|         _Faces = new(); | ||||
|         _Named = new(); | ||||
|         _Mapping = new(); | ||||
|         _Closest = new(); | ||||
|         _Changed = changed; | ||||
|         _Property = property; | ||||
|  | ||||
| @ -116,7 +116,7 @@ public class Location : Properties.ILocation, IEquatable<Location> | ||||
|         value = at / total; | ||||
|         if (value < 0) | ||||
|             value = 3; | ||||
|         result = (int)(Math.Round(value, Stateless.ILocation.Decimals) * Stateless.ILocation.Factor); | ||||
|         result = (int)(Math.Round(value, Stateless.ILocation.Digits) * Stateless.ILocation.Factor); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|  | ||||
							
								
								
									
										49
									
								
								Shared/Models/Mapping.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								Shared/Models/Mapping.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace View_by_Distance.Shared.Models; | ||||
|  | ||||
| public class Mapping : Properties.IMapping | ||||
| { | ||||
|  | ||||
|     protected readonly int? _ApproximateYears; | ||||
|     protected readonly string _DisplayDirectoryName; | ||||
|     protected bool? _Filtered; | ||||
|     protected readonly int? _NormalizedPixelPercentage; | ||||
|     protected readonly PersonBirthday _PersonBirthday; | ||||
|     protected readonly string _PersonKey; | ||||
|     public int? ApproximateYears => _ApproximateYears; | ||||
|     public string DisplayDirectoryName => _DisplayDirectoryName; | ||||
|     public bool? Filtered => _Filtered; | ||||
|     public int? NormalizedPixelPercentage => _NormalizedPixelPercentage; | ||||
|     public PersonBirthday PersonBirthday => _PersonBirthday; | ||||
|     public string PersonKey => _PersonKey; | ||||
|  | ||||
|     [JsonConstructor] | ||||
|     public Mapping(int? approximateYears, string displayDirectoryName, bool? filtered, int? normalizedPixelPercentage, PersonBirthday personBirthday, string personKey) | ||||
|     { | ||||
|         _ApproximateYears = approximateYears; | ||||
|         _DisplayDirectoryName = displayDirectoryName; | ||||
|         _Filtered = filtered; | ||||
|         _NormalizedPixelPercentage = normalizedPixelPercentage; | ||||
|         _PersonBirthday = personBirthday; | ||||
|         _PersonKey = personKey; | ||||
|     } | ||||
|  | ||||
|     public Mapping(int? approximateYears, string displayDirectoryName, int? normalizedPixelPercentage, PersonBirthday personBirthday, string personKey) : | ||||
|         this(approximateYears, displayDirectoryName, null, normalizedPixelPercentage, personBirthday, personKey) | ||||
|     { } | ||||
|  | ||||
|     public Mapping(int? approximateYears, string displayDirectoryName, PersonBirthday personBirthday, string personKey) : | ||||
|         this(approximateYears, displayDirectoryName, null, null, personBirthday, personKey) | ||||
|     { } | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public void SetFiltered() => _Filtered = true; | ||||
|  | ||||
| } | ||||
							
								
								
									
										48
									
								
								Shared/Models/MappingContainer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								Shared/Models/MappingContainer.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace View_by_Distance.Shared.Models; | ||||
|  | ||||
| public class MappingContainer : Properties.IMappingContainer | ||||
| { | ||||
|  | ||||
|     protected double? _Distance; | ||||
|     protected readonly Face? _Face; | ||||
|     protected readonly int _Id; | ||||
|     protected readonly bool? _IsWrongYear; | ||||
|     protected readonly string _Key; | ||||
|     protected readonly Mapping _Mapping; | ||||
|     protected readonly DateTime _MinimumDateTime; | ||||
|     public double? Distance => _Distance; | ||||
|     public Face? Face => _Face; | ||||
|     public int Id => _Id; | ||||
|     public bool? IsWrongYear => _IsWrongYear; | ||||
|     public string Key => _Key; | ||||
|     public Mapping Mapping => _Mapping; | ||||
|     public DateTime MinimumDateTime => _MinimumDateTime; | ||||
|  | ||||
|     [JsonConstructor] | ||||
|     public MappingContainer(double? distance, Face? face, int id, bool? isWrongYear, string key, Mapping mapping, DateTime minimumDateTime) | ||||
|     { | ||||
|         _Distance = distance; | ||||
|         _Face = face; | ||||
|         _Id = id; | ||||
|         _IsWrongYear = isWrongYear; | ||||
|         _Key = key; | ||||
|         _Mapping = mapping; | ||||
|         _MinimumDateTime = minimumDateTime; | ||||
|     } | ||||
|  | ||||
|     public MappingContainer(Face face, int id, bool? isWrongYear, string key, Mapping mapping, DateTime minimumDateTime) : | ||||
|         this(null, face, id, isWrongYear, key, mapping, minimumDateTime) | ||||
|     { } | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public void SetDistance(double v) => _Distance = v; | ||||
|  | ||||
| } | ||||
| @ -1,6 +1,6 @@ | ||||
| namespace View_by_Distance.Shared.Models.Methods; | ||||
|  | ||||
| public interface INamed : Stateless.Methods.INamed | ||||
| public interface INamed : Stateless.Methods.IMapping | ||||
| { // ... | ||||
|  | ||||
|     // | ||||
|  | ||||
| @ -1,37 +0,0 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace View_by_Distance.Shared.Models; | ||||
|  | ||||
| public class Named : Properties.INamed | ||||
| { | ||||
|  | ||||
|     protected readonly bool? _IsWrongYear; | ||||
|     protected readonly DateTime _MinimumDateTime; | ||||
|     protected readonly int? _NormalizedPixelPercentage; | ||||
|     protected readonly PersonBirthday? _PersonBirthday; | ||||
|     public bool? IsWrongYear => _IsWrongYear; | ||||
|     public DateTime MinimumDateTime => _MinimumDateTime; | ||||
|     public int? NormalizedPixelPercentage => _NormalizedPixelPercentage; | ||||
|     public PersonBirthday? PersonBirthday => _PersonBirthday; | ||||
|  | ||||
|     [JsonConstructor] | ||||
|     public Named(bool? isWrongYear, DateTime minimumDateTime, int? normalizedPixelPercentage, PersonBirthday? personBirthday) | ||||
|     { | ||||
|         _IsWrongYear = isWrongYear; | ||||
|         _MinimumDateTime = minimumDateTime; | ||||
|         _NormalizedPixelPercentage = normalizedPixelPercentage; | ||||
|         _PersonBirthday = personBirthday; | ||||
|     } | ||||
|  | ||||
|     public Named(bool? isWrongYear, DateTime minimumDateTime, PersonBirthday? personBirthday) : | ||||
|         this(isWrongYear, minimumDateTime, null, personBirthday) | ||||
|     { } | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -1,3 +1,4 @@ | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| @ -53,4 +54,18 @@ public class Person : Properties.IPerson | ||||
|         return result; | ||||
|     } // ... | ||||
|  | ||||
|     public string GetFullName() | ||||
|     { | ||||
|         StringBuilder result = new(); | ||||
|         if (_Name?.First is not null && !string.IsNullOrEmpty(_Name.First.Value)) | ||||
|             _ = result.Append(_Name.First.Value); | ||||
|         if (_Name?.Middle is not null && !string.IsNullOrEmpty(_Name.Middle.Value)) | ||||
|             _ = result.Append(' ').Append(_Name.Middle.Value); | ||||
|         if (_Name?.Last is not null && !string.IsNullOrEmpty(_Name.Last.Value)) | ||||
|             _ = result.Append(' ').Append(_Name.Last.Value); | ||||
|         if (_Name?.Alias is not null && !string.IsNullOrEmpty(_Name.Alias.Value)) | ||||
|             _ = result.Append(" (").Append(_Name.Alias.Value).Append(')'); | ||||
|         return result.ToString(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -3,11 +3,12 @@ namespace View_by_Distance.Shared.Models.Properties; | ||||
| public interface IClosest | ||||
| { | ||||
|  | ||||
|     public double? Average { get; } | ||||
|     public int? NormalizedPixelPercentage { get; } | ||||
|     public double Average { get; } | ||||
|     public bool? IsWrongYear { get; } | ||||
|     public double? Minimum { get; } | ||||
|     public Mapping Mapping { get; } | ||||
|     public double Minimum { get; } | ||||
|     public DateTime MinimumDateTime { get; } | ||||
|     public PersonBirthday? PersonBirthday { get; } | ||||
|     public int NormalizedPixelPercentage { get; } | ||||
|     public long? TicksDelta { get; } | ||||
|  | ||||
| } | ||||
| @ -4,6 +4,7 @@ public interface IFace | ||||
| { | ||||
|  | ||||
|     public DateTime DateTime { get; } | ||||
|     public List<FaceDistance> FaceDistances { get; } | ||||
|     public FaceEncoding? FaceEncoding { get; } | ||||
|     public Dictionary<Stateless.FacePart, FacePoint[]>? FaceParts { get; } | ||||
|     public Location? Location { get; } | ||||
|  | ||||
| @ -1,11 +1,12 @@ | ||||
| namespace View_by_Distance.Shared.Models.Properties; | ||||
| 
 | ||||
| public interface INamed | ||||
| public interface IFaceDistance | ||||
| { | ||||
| 
 | ||||
|     public List<double> Distances { get; } | ||||
|     public bool? IsWrongYear { get; } | ||||
|     public string Key { get; } | ||||
|     public Mapping Mapping { get; } | ||||
|     public DateTime MinimumDateTime { get; } | ||||
|     public int? NormalizedPixelPercentage { get; } | ||||
|     public PersonBirthday? PersonBirthday { get; } | ||||
| 
 | ||||
| } | ||||
| @ -8,9 +8,9 @@ public interface IItem | ||||
|     public List<Closest> Closest { get; } | ||||
|     public List<Face> Faces { get; } | ||||
|     public FileHolder? ImageFileHolder { get; } | ||||
|     public List<Mapping> Mapping { get; } | ||||
|     public bool? Moved { get; } | ||||
|     public bool? NoJson { get; } | ||||
|     public List<Named> Named { get; } | ||||
|     public Property? Property { get; } | ||||
|     public string RelativePath { get; } | ||||
|     public FileHolder? ResizedFileHolder { get; } | ||||
|  | ||||
							
								
								
									
										13
									
								
								Shared/Models/Properties/IMapping.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Shared/Models/Properties/IMapping.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| namespace View_by_Distance.Shared.Models.Properties; | ||||
|  | ||||
| public interface IMapping | ||||
| { | ||||
|  | ||||
|     public int? ApproximateYears { get; } | ||||
|     public string DisplayDirectoryName { get; } | ||||
|     public bool? Filtered { get; } | ||||
|     public int? NormalizedPixelPercentage { get; } | ||||
|     public PersonBirthday PersonBirthday { get; } | ||||
|     public string PersonKey { get; } | ||||
|  | ||||
| } | ||||
							
								
								
									
										14
									
								
								Shared/Models/Properties/IMappingContainer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Shared/Models/Properties/IMappingContainer.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| namespace View_by_Distance.Shared.Models.Properties; | ||||
|  | ||||
| public interface IMappingContainer | ||||
| { | ||||
|  | ||||
|     public double? Distance { get; } | ||||
|     public Face? Face { get; } | ||||
|     public int Id { get; } | ||||
|     public bool? IsWrongYear { get; } | ||||
|     public string Key { get; } | ||||
|     public Mapping Mapping { get; } | ||||
|     public DateTime MinimumDateTime { get; } | ||||
|  | ||||
| } | ||||
| @ -3,9 +3,14 @@ | ||||
| public interface IClosest | ||||
| { | ||||
|  | ||||
|     // 637972153144596958 | ||||
|     // const int Digits = 3; | ||||
|     // const int Factor = 1000; | ||||
|     // const int MaximumPer = 50; | ||||
|     // const bool SkipIsWrongYear = false; | ||||
|  | ||||
|     const int Digits = 3; | ||||
|     const int Factor = 1000; | ||||
|     const int MaximumPer = 50; | ||||
|     const float MaximumMinimum = 0.50f; | ||||
|     const bool SkipIsWrongYear = true; | ||||
|     const float MinimumMinimum = 0.05f; | ||||
|  | ||||
| } | ||||
							
								
								
									
										11
									
								
								Shared/Models/Stateless/IFaceDistance.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Shared/Models/Stateless/IFaceDistance.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| namespace View_by_Distance.Shared.Models.Stateless; | ||||
|  | ||||
| public interface IFaceDistance | ||||
| { | ||||
|     // 637972153144596958 | ||||
|     // const int MaximumPer = 999; | ||||
|  | ||||
|     const int MaximumPer = 9999; | ||||
|     const double Tolerance = 0.6d; | ||||
|  | ||||
| } | ||||
| @ -3,7 +3,7 @@ | ||||
| public interface ILocation | ||||
| { | ||||
|  | ||||
|     const int Decimals = 6; | ||||
|     const int Digits = 6; | ||||
|     const int Factor = 1000000; | ||||
|  | ||||
| } | ||||
							
								
								
									
										14
									
								
								Shared/Models/Stateless/IMapping.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								Shared/Models/Stateless/IMapping.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| namespace View_by_Distance.Shared.Models.Stateless; | ||||
|  | ||||
| public interface IMapping | ||||
| { | ||||
|  | ||||
|     // 637972153144596958 | ||||
|     // const bool UseDeterministicHashCodeUnknownFaceKeyValuePairsForAddToNamed = true; | ||||
|     // const bool OnlyUseNamedWithNormalizedPixelPercentagePopulatedForGetKeyValuePairs = false; | ||||
|  | ||||
|     const bool UseDeterministicHashCodeUnknownFaceKeyValuePairsForAddToMapping = false; | ||||
|     const bool UseDeterministicHashCodeUnknownFaceKeyValuePairsForSaveMapping = false; | ||||
|     const bool SaveFaceEncoding = false; | ||||
|  | ||||
| } | ||||
							
								
								
									
										8
									
								
								Shared/Models/Stateless/IPersonBirthday.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Shared/Models/Stateless/IPersonBirthday.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| namespace View_by_Distance.Shared.Models.Stateless; | ||||
|  | ||||
| public interface IPersonBirthday | ||||
| { | ||||
|  | ||||
|     const string Format = "yyyy-MM-dd_HH"; | ||||
|  | ||||
| } | ||||
							
								
								
									
										22
									
								
								Shared/Models/Stateless/Methods/Age.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Shared/Models/Stateless/Methods/Age.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| namespace View_by_Distance.Shared.Models.Stateless.Methods; | ||||
|  | ||||
| internal abstract class Age | ||||
| { | ||||
|  | ||||
|     internal static (int, TimeSpan) GetAge(DateTime minuend, DateTime subtrahend) | ||||
|     { | ||||
|         TimeSpan result; | ||||
|         int years = 0; | ||||
|         DateTime check = new(subtrahend.Ticks); | ||||
|         for (int i = 0; i < int.MaxValue; i++) | ||||
|         { | ||||
|             check = check.AddYears(1); | ||||
|             if (check > minuend) | ||||
|                 break; | ||||
|             years += 1; | ||||
|         } | ||||
|         result = new(minuend.Ticks - check.AddYears(-1).Ticks); | ||||
|         return (years, result); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -3,6 +3,33 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; | ||||
| internal abstract class Closest | ||||
| { | ||||
|  | ||||
|     internal static Models.Closest[] Get(List<Models.Closest> collection) => (from l in collection orderby l.Minimum < Stateless.IClosest.MinimumMinimum, l.Average select l).ToArray(); | ||||
|     private static int Get(List<double> faceDistances) => (int)(Math.Round(faceDistances.Average(), Stateless.IClosest.Digits) * Stateless.ILocation.Factor); | ||||
|  | ||||
|     private static Models.Closest Get(Models.Face face, DateTime minimumDateTime, FaceDistance faceDistance) | ||||
|     { | ||||
|         Models.Closest result; | ||||
|         int average = Get(faceDistance.Distances); | ||||
|         double minimum = faceDistance.Distances.Min(); | ||||
|         long? ticksDelta; | ||||
|         if (faceDistance.IsWrongYear is null || faceDistance.IsWrongYear.Value) | ||||
|             ticksDelta = null; | ||||
|         else | ||||
|         { | ||||
|             ticksDelta = Math.Abs(faceDistance.MinimumDateTime.Ticks - minimumDateTime.Ticks); | ||||
|             if (faceDistance.MinimumDateTime < faceDistance.Mapping.PersonBirthday.Value) | ||||
|                 ticksDelta *= 2; | ||||
|         } | ||||
|         if (face.Location?.NormalizedPixelPercentage is null) | ||||
|             throw new NullReferenceException(nameof(face.Location.NormalizedPixelPercentage)); | ||||
|         result = new(average, face.Location.NormalizedPixelPercentage.Value, faceDistance.IsWrongYear, faceDistance.Mapping, minimum, faceDistance.MinimumDateTime, ticksDelta); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     internal static Models.Closest[] GetCollection(Models.Face face, DateTime minimumDateTime, List<FaceDistance> faceDistances) | ||||
|     { | ||||
|         Models.Closest[] results; | ||||
|         Models.Closest[] closestCollection = (from l in faceDistances select Get(face, minimumDateTime, l)).ToArray(); | ||||
|         results = (from l in closestCollection orderby l.Average, l.TicksDelta.HasValue, l.TicksDelta select l).ToArray(); | ||||
|         return results; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										9
									
								
								Shared/Models/Stateless/Methods/IAge.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Shared/Models/Stateless/Methods/IAge.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| namespace View_by_Distance.Shared.Models.Stateless.Methods; | ||||
|  | ||||
| public interface IAge | ||||
| { // ... | ||||
|  | ||||
|     (int, TimeSpan) TestStatic_GetAge(DateTime minuend, DateTime subtrahend); | ||||
|     static (int, TimeSpan) GetAge(DateTime minuend, DateTime subtrahend) => Age.GetAge(minuend, subtrahend); | ||||
|  | ||||
| } | ||||
| @ -3,8 +3,8 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; | ||||
| public interface IClosest | ||||
| { // ... | ||||
|  | ||||
|     Models.Closest[] TestStatic_Get(List<Models.Closest> collection); | ||||
|     Models.Closest[] TestStatic_Get(List<FaceDistance> faceDistances); | ||||
|  | ||||
|     static Models.Closest[] Get(List<Models.Closest> collection) => Closest.Get(collection); | ||||
|     static Models.Closest[] GetCollection(Models.Face face, DateTime minimumDateTime, List<FaceDistance> faceDistances) => Closest.GetCollection(face, minimumDateTime, faceDistances); | ||||
|  | ||||
| } | ||||
| @ -1,18 +1,18 @@ | ||||
| namespace View_by_Distance.Shared.Models.Stateless.Methods; | ||||
| 
 | ||||
| public interface INamed | ||||
| public interface IMapping | ||||
| { // ... | ||||
| 
 | ||||
|     double? TestStatic_GetReversedDeterministicHashCodeKey(string file); | ||||
|     static double? GetReversedDeterministicHashCodeKey(bool keyValuePairsAny, Dictionary<int, List<Models.Face>> keyValuePairs, string file) => | ||||
|         Named.GetReversedDeterministicHashCodeKey(keyValuePairsAny, keyValuePairs, file); | ||||
|         Mapping.GetReversedDeterministicHashCodeKey(keyValuePairsAny, keyValuePairs, file); | ||||
| 
 | ||||
|     double TestStatic_GetDeterministicHashCodeKey(Models.Item item, Models.Face face); | ||||
|     static double GetDeterministicHashCodeKey(Models.Item item, Models.Face face) => | ||||
|         Named.GetDeterministicHashCodeKey(item, face); | ||||
|         Mapping.GetDeterministicHashCodeKey(item, face); | ||||
| 
 | ||||
|     double TestStatic_GetDeterministicHashCodeKey(Models.Item item, Models.Closest closest); | ||||
|     static double GetDeterministicHashCodeKey(Models.Item item, Models.Closest closest) => | ||||
|         Named.GetDeterministicHashCodeKey(item, closest); | ||||
|         Mapping.GetDeterministicHashCodeKey(item, closest); | ||||
| 
 | ||||
| } | ||||
| @ -3,37 +3,65 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; | ||||
| public interface IPersonBirthday | ||||
| { | ||||
|  | ||||
|     DateTime TestStatic_GetDefaultValue() => PersonBirthday.GetDefaultValue(); // {{1}}SingletonValue | ||||
|     DateTime TestStatic_GetDefaultValue() => | ||||
|         PersonBirthday.GetDefaultValue(); // {{1}}SingletonValue | ||||
|  | ||||
|     static DateTime GetDefaultValue() => PersonBirthday.GetDefaultValue(); // {{1}}SingletonValue | ||||
|     static DateTime GetDefaultValue() => | ||||
|         PersonBirthday.GetDefaultValue(); // {{1}}SingletonValue | ||||
|  | ||||
|     // ... | ||||
|  | ||||
|     string TestStatic_GetFormat() => PersonBirthday.GetFormat(); | ||||
|     static string GetFormat() => PersonBirthday.GetFormat(); | ||||
|     double? TestStatic_GetAge(Models.PersonBirthday birthday); | ||||
|     static double? GetAge(Models.PersonBirthday birthday) => | ||||
|         PersonBirthday.GetAge(birthday); | ||||
|  | ||||
|     DateTime? TestStatic_GetDateTime(string personKey) => PersonBirthday.GetDateTime(personKey); | ||||
|     static DateTime? GetDateTime(string personKey) => PersonBirthday.GetDateTime(personKey); | ||||
|     DateTime? TestStatic_GetDateTime(string personKey) => | ||||
|         PersonBirthday.GetDateTime(personKey); | ||||
|     static DateTime? GetDateTime(string personKey) => | ||||
|         PersonBirthday.GetDateTime(personKey); | ||||
|  | ||||
|     string TestStatic_GetFileName(Models.PersonBirthday personBirthday) => PersonBirthday.GetFileName(personBirthday); | ||||
|     static string GetFileName(Models.PersonBirthday personBirthday) => PersonBirthday.GetFileName(personBirthday); | ||||
|     string TestStatic_GetFileName(Models.PersonBirthday personBirthday) => | ||||
|         PersonBirthday.GetFileName(personBirthday); | ||||
|     static string GetFileName(Models.PersonBirthday personBirthday) => | ||||
|         PersonBirthday.GetFileName(personBirthday); | ||||
|  | ||||
|     Models.PersonBirthday? TestStatic_GetPersonBirthday(string personKey) => PersonBirthday.GetPersonBirthday(personKey); | ||||
|     static Models.PersonBirthday? GetPersonBirthday(string personKey) => PersonBirthday.GetPersonBirthday(personKey); | ||||
|     (int, TimeSpan) TestStatic_GetAge(DateTime dateTime, Models.PersonBirthday birthday); | ||||
|     static (int, TimeSpan) GetAge(DateTime dateTime, Models.PersonBirthday birthday) => | ||||
|         PersonBirthday.GetAge(dateTime, birthday); | ||||
|  | ||||
|     string TestStatic_GetFormatted(Models.PersonBirthday personBirthday) => PersonBirthday.GetFormatted(personBirthday); | ||||
|     static string GetFormatted(Models.PersonBirthday personBirthday) => PersonBirthday.GetFormatted(personBirthday); | ||||
|     string TestStatic_GetFormatted(Models.PersonBirthday personBirthday) => | ||||
|         PersonBirthday.GetFormatted(personBirthday); | ||||
|     static string GetFormatted(Models.PersonBirthday personBirthday) => | ||||
|         PersonBirthday.GetFormatted(personBirthday); | ||||
|  | ||||
|     Models.PersonBirthday TestStatic_GetNextBirthDate(Properties.IStorage storage) => PersonBirthday.GetNextBirthDate(storage); | ||||
|     static Models.PersonBirthday GetNextBirthDate(Properties.IStorage storage) => PersonBirthday.GetNextBirthDate(storage); | ||||
|     Models.PersonBirthday? TestStatic_GetPersonBirthday(string personKey) => | ||||
|         PersonBirthday.GetPersonBirthday(personKey); | ||||
|     static Models.PersonBirthday? GetPersonBirthday(string personKey) => | ||||
|         PersonBirthday.GetPersonBirthday(personKey); | ||||
|  | ||||
|     bool TestStatic_DoesBirthDateExits(Properties.IStorage storage, Models.PersonBirthday personBirthday) => DoesBirthDateExits(storage, personBirthday); | ||||
|     static bool DoesBirthDateExits(Properties.IStorage storage, Models.PersonBirthday personBirthday) => DoesBirthDateExits(storage, personBirthday); | ||||
|     Models.PersonBirthday TestStatic_GetNextBirthDate(Properties.IStorage storage) => | ||||
|         PersonBirthday.GetNextBirthDate(storage); | ||||
|     static Models.PersonBirthday GetNextBirthDate(Properties.IStorage storage) => | ||||
|         PersonBirthday.GetNextBirthDate(storage); | ||||
|  | ||||
|     string TestStatic_GetFileFullName(Properties.IStorage storage, Models.PersonBirthday personBirthday) => PersonBirthday.GetFileFullName(storage, personBirthday); | ||||
|     static string GetFileFullName(Properties.IStorage storage, Models.PersonBirthday personBirthday) => PersonBirthday.GetFileFullName(storage, personBirthday); | ||||
|     TimeSpan? TestStatic_Get(DateTime now, Models.PersonBirthday personBirthday) => | ||||
|         PersonBirthday.GetTimeSpan(now, isWrongYear: false, personBirthday); | ||||
|     static TimeSpan? GetTimeSpan(DateTime minimumDateTime, Models.PersonBirthday personBirthday) => | ||||
|         PersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear: false, personBirthday); | ||||
|  | ||||
|     TimeSpan? TestStatic_Get(DateTime minimumDateTime, bool? isWrongYear, Models.PersonBirthday personBirthday) => PersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear, personBirthday); | ||||
|     static TimeSpan? GetTimeSpan(DateTime minimumDateTime, bool? isWrongYear, Models.PersonBirthday personBirthday) => PersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear, personBirthday); | ||||
|     string TestStatic_GetFileFullName(Properties.IStorage storage, Models.PersonBirthday personBirthday) => | ||||
|         PersonBirthday.GetFileFullName(storage, personBirthday); | ||||
|     static string GetFileFullName(Properties.IStorage storage, Models.PersonBirthday personBirthday) => | ||||
|         PersonBirthday.GetFileFullName(storage, personBirthday); | ||||
|  | ||||
|     bool TestStatic_DoesBirthDateExits(Properties.IStorage storage, Models.PersonBirthday personBirthday) => | ||||
|         DoesBirthDateExits(storage, personBirthday); | ||||
|     static bool DoesBirthDateExits(Properties.IStorage storage, Models.PersonBirthday personBirthday) => | ||||
|         DoesBirthDateExits(storage, personBirthday); | ||||
|  | ||||
|     TimeSpan? TestStatic_Get(DateTime minimumDateTime, bool? isWrongYear, Models.PersonBirthday personBirthday) => | ||||
|         PersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear, personBirthday); | ||||
|     static TimeSpan? GetTimeSpan(DateTime minimumDateTime, bool? isWrongYear, Models.PersonBirthday personBirthday) => | ||||
|         PersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear, personBirthday); | ||||
|  | ||||
| } | ||||
| @ -2,18 +2,18 @@ using System.Text.Json; | ||||
| 
 | ||||
| namespace View_by_Distance.Shared.Models.Stateless.Methods; | ||||
| 
 | ||||
| internal abstract class Named | ||||
| internal abstract class Mapping | ||||
| { | ||||
| 
 | ||||
|     private static double GetDeterministicHashCodeKey(int id, int normalizedPixelPercentage) | ||||
|         => Math.Round(double.Parse($"{id}.{normalizedPixelPercentage}"), Stateless.ILocation.Decimals); | ||||
|         => Math.Round(double.Parse($"{id}.{normalizedPixelPercentage}"), Stateless.ILocation.Digits); | ||||
| 
 | ||||
|     internal static double GetDeterministicHashCodeKey(Models.Item item, Models.Closest closest) | ||||
|     { | ||||
|         double result; | ||||
|         if (item.Property?.Id is null || item.ImageFileHolder is null || closest.NormalizedPixelPercentage is null) | ||||
|         if (item.Property?.Id is null || item.ImageFileHolder is null) | ||||
|             throw new NullReferenceException(); | ||||
|         result = GetDeterministicHashCodeKey(item.Property.Id.Value, closest.NormalizedPixelPercentage.Value); | ||||
|         result = GetDeterministicHashCodeKey(item.Property.Id.Value, closest.NormalizedPixelPercentage); | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
| @ -1,4 +1,5 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.RegularExpressions; | ||||
|  | ||||
| namespace View_by_Distance.Shared.Models.Stateless.Methods; | ||||
|  | ||||
| @ -166,9 +167,9 @@ internal abstract class Person | ||||
|         Models.PersonName name; | ||||
|         List<Models.PersonURL> urls; | ||||
|         Models.PersonBirthday birthday; | ||||
|         List<Models.PersonComment> comments; | ||||
|         List<Models.PersonEmail> emails = new(); | ||||
|         List<Models.PersonNumber> numbers = new(); | ||||
|         List<Models.PersonComment> comments = new(); | ||||
|         List<Models.PersonAddress> addresses = new(); | ||||
|         Dictionary<DateTime, PersonImport> keyValuePairs = GetPersonCollection(localKnownPeopleFile); | ||||
|         foreach (KeyValuePair<DateTime, PersonImport> keyValuePair in keyValuePairs) | ||||
| @ -176,6 +177,7 @@ internal abstract class Person | ||||
|             if (string.IsNullOrEmpty(keyValuePair.Value.Name)) | ||||
|                 continue; | ||||
|             urls = new(); | ||||
|             comments = new(); | ||||
|             birthday = new(keyValuePair.Key); | ||||
|             name = PersonName.Create(keyValuePair.Value.Name); | ||||
|             if (name.First is null || string.IsNullOrEmpty(name.First.Value)) | ||||
| @ -242,7 +244,54 @@ internal abstract class Person | ||||
|                 results = GetPeopleFromText(storage, localKnownPeopleFile); | ||||
|             } | ||||
|         } | ||||
|         SaveToDirectory(storage, results); | ||||
|         return results.ToArray(); | ||||
|     } | ||||
|  | ||||
|     private static void SaveToDirectory(Properties.IStorage storage, List<Models.Person> people) | ||||
|     { | ||||
|         int years; | ||||
|         TimeSpan? timeSpan; | ||||
|         string personDirectory; | ||||
|         string? personFullName; | ||||
|         DateTime createdDateTime; | ||||
|         string birthdayDirectory; | ||||
|         string personJsonFileName; | ||||
|         string personDirectoryName; | ||||
|         string? peopleDirectory = null; | ||||
|         DateTime dateTime = DateTime.Now; | ||||
|         string? personJsonFileNameWithoutExtension; | ||||
|         const string pattern = @"[\\,\/,\:,\*,\?,\"",\<,\>,\|]"; | ||||
|         foreach (Models.Person person in people) | ||||
|         { | ||||
|             personJsonFileName = IPerson.GetFileFullName(storage, person); | ||||
|             if (string.IsNullOrEmpty(peopleDirectory)) | ||||
|                 peopleDirectory = Path.GetDirectoryName(personJsonFileName); | ||||
|             if (string.IsNullOrEmpty(peopleDirectory)) | ||||
|                 break; | ||||
|             personJsonFileNameWithoutExtension = Path.GetFileNameWithoutExtension(personJsonFileName); | ||||
|             if (string.IsNullOrEmpty(personJsonFileNameWithoutExtension)) | ||||
|                 break; | ||||
|             personFullName = Regex.Replace(person.GetFullName(), pattern, string.Empty); | ||||
|             timeSpan = IPersonBirthday.GetTimeSpan(dateTime, person.Birthday); | ||||
|             if (timeSpan is null || timeSpan.Value.Ticks < 0) | ||||
|                 personDirectoryName = $"{personFullName}~"; | ||||
|             else | ||||
|             { | ||||
|                 createdDateTime = new FileInfo(personJsonFileName).CreationTime; | ||||
|                 (years, timeSpan) = IPersonBirthday.GetAge(createdDateTime, person.Birthday); | ||||
|                 personDirectoryName = $"{personFullName}^{years}-{Math.Floor(timeSpan.Value.TotalDays):000}"; | ||||
|             } | ||||
|             personDirectory = Path.Combine(peopleDirectory, personDirectoryName); | ||||
|             if (!Directory.Exists(personDirectory)) | ||||
|                 _ = Directory.CreateDirectory(personDirectory); | ||||
|             birthdayDirectory = Path.Combine(personDirectory, personJsonFileNameWithoutExtension); | ||||
|             if (!Directory.Exists(birthdayDirectory)) | ||||
|             { | ||||
|                 _ = Directory.CreateDirectory(birthdayDirectory); | ||||
|                 File.Copy(personJsonFileName, Path.Combine(birthdayDirectory, $"{personJsonFileNameWithoutExtension}.json")); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -8,14 +8,12 @@ internal abstract class PersonBirthday | ||||
|     internal static DateTime GetDefaultValue() => DateTime.MinValue; // {{1}}SingletonValue | ||||
|  | ||||
|     // ... | ||||
|  | ||||
|     internal static string GetFormat() => "yyyy-MM-dd_HH"; | ||||
|     internal static string GetFormatted(Models.PersonBirthday personBirthday) => personBirthday.Value.ToString(GetFormat()); | ||||
|     internal static string GetFileName(Models.PersonBirthday personBirthday) => $"{personBirthday.Value.ToString(GetFormat())}.json"; | ||||
|     internal static string GetFormatted(Models.PersonBirthday personBirthday) => personBirthday.Value.ToString(Stateless.IPersonBirthday.Format); | ||||
|     internal static string GetFileName(Models.PersonBirthday personBirthday) => $"{personBirthday.Value.ToString(Stateless.IPersonBirthday.Format)}.json"; | ||||
|     internal static bool DoesBirthDateExits(Properties.IStorage storage, Models.PersonBirthday personBirthday) => File.Exists(GetFileFullName(storage, personBirthday)); | ||||
|     internal static Models.PersonBirthday GetNextBirthDate(Properties.IStorage storage) => throw new Exception(storage.ToString()); // Person.GetNextBirthDate(storage); | ||||
|     internal static string GetFileFullName(Properties.IStorage storage, Models.PersonBirthday personBirthday) => Path.Combine(storage.PeopleRootDirectory, "{}", GetFileName(personBirthday)); | ||||
|     internal static DateTime? GetDateTime(string personKey) => DateTime.TryParseExact(personKey, GetFormat(), CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateTime) ? dateTime : null; | ||||
|     internal static DateTime? GetDateTime(string personKey) => DateTime.TryParseExact(personKey, Stateless.IPersonBirthday.Format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateTime) ? dateTime : null; | ||||
|  | ||||
|     internal static Models.PersonBirthday? GetPersonBirthday(string personKey) | ||||
|     { | ||||
| @ -37,4 +35,41 @@ internal abstract class PersonBirthday | ||||
|             timeSpan = new(minimumDateTime.Ticks - personBirthday.Value.Ticks); | ||||
|         return timeSpan; | ||||
|     } | ||||
|  | ||||
|     internal static (int, TimeSpan) GetAge(DateTime dateTime, Models.PersonBirthday birthday) | ||||
|     { | ||||
|         TimeSpan result; | ||||
|         int years; | ||||
|         if (birthday?.Value is null) | ||||
|             throw new NullReferenceException(nameof(birthday.Value)); | ||||
|         (years, result) = Age.GetAge(dateTime, birthday.Value); | ||||
|         return (years, result); | ||||
|     } | ||||
|  | ||||
|     internal static (int, double) GetAge(DateTime dateTime, DateTime dayBeforeLeapDate, Models.PersonBirthday birthday) | ||||
|     { | ||||
|         double result; | ||||
|         (int years, TimeSpan timeSpan) = GetAge(dateTime, birthday); | ||||
|         if (!DateTime.IsLeapYear(dateTime.Year) || dateTime < dayBeforeLeapDate.AddDays(1)) | ||||
|             result = timeSpan.TotalDays / 365; | ||||
|         else | ||||
|             result = timeSpan.TotalDays / 366; | ||||
|         return (years, result); | ||||
|     } | ||||
|  | ||||
|     internal static double? GetAge(Models.PersonBirthday birthday) | ||||
|     { | ||||
|         double? result; | ||||
|         if (birthday is null) | ||||
|             result = null; | ||||
|         else | ||||
|         { | ||||
|             DateTime dateTime = DateTime.Now; | ||||
|             DateTime dayBeforeLeapDate = new(dateTime.Year, 2, 28); | ||||
|             (int years, double r) = GetAge(dateTime, dayBeforeLeapDate, birthday); | ||||
|             result = years + r; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -62,10 +62,10 @@ internal abstract class Property | ||||
|             in segments | ||||
|             where l?.Length > 2 | ||||
|             && ( | ||||
|                 l[..2] is "19" or "20" | ||||
|             || (l.Length == 5 && l.Substring(1, 2) is "19" or "20" && (l[0] is '~' or '=' or '-' or '^' or '#')) | ||||
|             || (l.Length == 6 && l[..2] is "19" or "20" && l[4] == '.') | ||||
|             || (l.Length == 7 && l.Substring(1, 2) is "19" or "20" && l[5] == '.') | ||||
|                 l[..2] is "18" or "19" or "20" | ||||
|             || (l.Length == 5 && l.Substring(1, 2) is "18" or "19" or "20" && (l[0] is '~' or '=' or '-' or '^' or '#')) | ||||
|             || (l.Length == 6 && l[..2] is "18" or "19" or "20" && l[4] == '.') | ||||
|             || (l.Length == 7 && l.Substring(1, 2) is "18" or "19" or "20" && l[5] == '.') | ||||
|             ) | ||||
|             select l | ||||
|         ).ToArray(); | ||||
|  | ||||
							
								
								
									
										80
									
								
								Tests/UnitTestCalculations.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								Tests/UnitTestCalculations.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | ||||
| using Microsoft.Extensions.Configuration; | ||||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
| using Phares.Shared; | ||||
| using Serilog; | ||||
| using System.Diagnostics; | ||||
| using System.Reflection; | ||||
| using View_by_Distance.Shared.Models.Stateless.Methods; | ||||
| using View_by_Distance.Tests.Models; | ||||
|  | ||||
| namespace View_by_Distance.Tests; | ||||
|  | ||||
| [TestClass] | ||||
| public class UnitTestCalculations | ||||
| { | ||||
|  | ||||
|     private readonly ILogger _Logger; | ||||
|     private readonly AppSettings _AppSettings; | ||||
|     private readonly string _WorkingDirectory; | ||||
|     private readonly Configuration _Configuration; | ||||
|     private readonly IsEnvironment _IsEnvironment; | ||||
|     private readonly IConfigurationRoot _ConfigurationRoot; | ||||
|     private readonly Property.Models.Configuration _PropertyConfiguration; | ||||
|  | ||||
|     public UnitTestCalculations() | ||||
|     { | ||||
|         ILogger logger; | ||||
|         AppSettings appSettings; | ||||
|         string workingDirectory; | ||||
|         Configuration configuration; | ||||
|         IsEnvironment isEnvironment; | ||||
|         IConfigurationRoot configurationRoot; | ||||
|         LoggerConfiguration loggerConfiguration = new(); | ||||
|         Property.Models.Configuration propertyConfiguration; | ||||
|         Assembly assembly = Assembly.GetExecutingAssembly(); | ||||
|         bool debuggerWasAttachedAtLineZero = Debugger.IsAttached || assembly.Location.Contains(@"\bin\Debug"); | ||||
|         isEnvironment = new(processesCount: null, nullASPNetCoreEnvironmentIsDevelopment: debuggerWasAttachedAtLineZero, nullASPNetCoreEnvironmentIsProduction: !debuggerWasAttachedAtLineZero); | ||||
|         IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() | ||||
|             .AddEnvironmentVariables() | ||||
|             .AddJsonFile(isEnvironment.AppSettingsFileName); | ||||
|         configurationRoot = configurationBuilder.Build(); | ||||
|         appSettings = Models.Binder.AppSettings.Get(configurationRoot); | ||||
|         workingDirectory = IWorkingDirectory.GetWorkingDirectory(assembly.GetName().Name, appSettings.WorkingDirectoryName); | ||||
|         Environment.SetEnvironmentVariable(nameof(workingDirectory), workingDirectory); | ||||
|         _ = ConfigurationLoggerConfigurationExtensions.Configuration(loggerConfiguration.ReadFrom, configurationRoot); | ||||
|         Log.Logger = loggerConfiguration.CreateLogger(); | ||||
|         logger = Log.ForContext<UnitTestCalculations>(); | ||||
|         propertyConfiguration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot); | ||||
|         configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration); | ||||
|         logger.Information("Complete"); | ||||
|         _Logger = logger; | ||||
|         _AppSettings = appSettings; | ||||
|         _Configuration = configuration; | ||||
|         _IsEnvironment = isEnvironment; | ||||
|         _WorkingDirectory = workingDirectory; | ||||
|         _ConfigurationRoot = configurationRoot; | ||||
|         _PropertyConfiguration = propertyConfiguration; | ||||
|     } | ||||
|  | ||||
|     [TestMethod] | ||||
|     public void TestMethodNull() | ||||
|     { | ||||
|         Assert.IsFalse(_Logger is null); | ||||
|         Assert.IsFalse(_AppSettings is null); | ||||
|         Assert.IsFalse(_Configuration is null); | ||||
|         Assert.IsFalse(_IsEnvironment is null); | ||||
|         Assert.IsFalse(_WorkingDirectory is null); | ||||
|         Assert.IsFalse(_ConfigurationRoot is null); | ||||
|         Assert.IsFalse(_PropertyConfiguration is null); | ||||
|     } | ||||
|  | ||||
|     [TestMethod] | ||||
|     public void TestMethodGetAge() | ||||
|     { | ||||
|         Shared.Models.PersonBirthday personBirthday = new(new(1980, 1, 17)); | ||||
|         double? age = IPersonBirthday.GetAge(personBirthday); | ||||
|         Assert.IsNotNull(age); | ||||
|         Assert.IsTrue(age.Value > 42.6092); | ||||
|     } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user