using Humanizer; using ShellProgressBar; using System.Text.Json; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Map.Models.Stateless; internal abstract class MapLogic { private static List AddToPersonKeysThenGetNonSpecificPeopleCollection(Configuration configuration, List personKeys) { List results = new(); Person person; long personKey; int? approximateYears = null; PersonBirthday personBirthday; PersonContainer personContainer; string[] personDisplayDirectoryAllFiles = Array.Empty(); DateTime incrementDate = new(configuration.PersonBirthdayFirstYear, 1, 1); for (int i = 0; i < 500; i++) { personKey = incrementDate.Ticks; incrementDate = incrementDate.AddDays(1); if (personKeys.Contains(personKey)) continue; personKeys.Add(personKey); personBirthday = IPersonBirthday.GetPersonBirthday(personKey); person = IPerson.GetPerson(configuration.MappingDefaultName, personKey, personBirthday); personContainer = new(approximateYears, person, new PersonBirthday[] { personBirthday }, personDisplayDirectoryAllFiles, configuration.MappingDefaultName, personKey); results.Add(personContainer); } return results; } private static void SetPersonCollections(Configuration configuration, string facesFileNameExtension, List personContainers, List personKeys, Dictionary personKeyFormattedToNewestPersonKeyFormatted, List personKeyFormattedCollection, Dictionary> skipCollection) { int? id; long personKey; string personKeyFormatted; int? normalizedPixelPercentage; string newestPersonKeyFormatted; foreach (PersonContainer personContainer in personContainers) { foreach (string personDisplayDirectoryAllFile in personContainer.DisplayDirectoryAllFiles) { if (!personDisplayDirectoryAllFile.EndsWith(facesFileNameExtension)) continue; (id, normalizedPixelPercentage, _) = IMapping.GetReversedDeterministicHashCodeKey(configuration.LocationDigits, facesFileNameExtension, personDisplayDirectoryAllFile); if (id is null || normalizedPixelPercentage is null) continue; if (!skipCollection.ContainsKey(id.Value)) skipCollection.Add(id.Value, new()); skipCollection[id.Value].Add(normalizedPixelPercentage.Value); } if (personContainer.Person is null || personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any()) continue; foreach (PersonBirthday personBirthday in personContainer.Birthdays) { personKey = personBirthday.Value.Ticks; personKeys.Add(personKey); } foreach (PersonBirthday personBirthday in personContainer.Birthdays) { personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personBirthday); personKeyFormattedCollection.Add(personKeyFormatted); if (personContainer.Birthdays.Length < 1) continue; newestPersonKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personContainer.Key.Value); if (!personKeyFormattedToNewestPersonKeyFormatted.ContainsKey(personKeyFormatted)) personKeyFormattedToNewestPersonKeyFormatted.Add(personKeyFormatted, newestPersonKeyFormatted); } } } internal static List<(string, string[], string, List?)> DeleteEmptyDirectoriesAndGetCollection(Configuration configuration, string facesFileNameExtension, long ticks, string eDistanceContentDirectory, List personKeyFormattedCollection, Dictionary> keyValuePairs) { List<(string, string[], string, List?)> results = new(); int? id; bool check; string[] files; List? faces; const int zero = 0; List checks = new(); string[] yearDirectories; string personKeyFormatted; string ticksDirectoryName; string? personFirstInitial; DirectoryInfo directoryInfo; string[] personKeyDirectories; int? normalizedPixelPercentage; string[] personNameDirectories; string[] personNameLinkDirectories; string? personFirstInitialDirectory; string[] personDisplayDirectoryNames; bool keyValuePairsAny = keyValuePairs.Any(); string manualCopyHumanized = nameof(IMapLogic.ManualCopy).Humanize(LetterCasing.Title); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string forceSingleImageHumanized = nameof(IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title); string[] ticksDirectories = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly); string message = $") {ticksDirectories.Length:000} compile from and clean ticks Director(ies) - A - {totalSeconds} total second(s)"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using ProgressBar progressBar = new(ticksDirectories.Length, message, options); foreach (string ticksDirectory in ticksDirectories) { progressBar.Tick(); ticksDirectoryName = Path.GetFileName(ticksDirectory); if (ticksDirectoryName.Length < 3 || ticksDirectoryName[zero] != '(' || ticksDirectoryName[^1] != ')') continue; if (!long.TryParse(ticksDirectoryName[1..^1], out long directoryTicks)) { if (!long.TryParse(ticksDirectoryName[1..^4], out directoryTicks)) continue; } directoryInfo = new(ticksDirectory); if (directoryInfo.CreationTime.Ticks != directoryTicks) Directory.SetCreationTime(ticksDirectory, new DateTime(directoryTicks)); if (directoryInfo.LastWriteTime.Ticks != directoryTicks) Directory.SetLastWriteTime(ticksDirectory, new DateTime(directoryTicks)); personKeyDirectories = Directory.GetDirectories(ticksDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string personKeyDirectory in personKeyDirectories) { personKeyFormatted = Path.GetFileName(personKeyDirectory); 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) { personDisplayDirectoryNames = IPath.GetDirectoryNames(personNameDirectory); if (!personDisplayDirectoryNames.Any()) continue; if (personDisplayDirectoryNames[^1].Length == 1 || personDisplayDirectoryNames[^1] == configuration.MappingDefaultName || !personKeyFormattedCollection.Contains(personKeyFormatted)) personFirstInitialDirectory = personNameDirectory; else { personFirstInitial = personDisplayDirectoryNames[^1][..1]; personFirstInitialDirectory = Path.Combine(yearDirectory, personFirstInitial.ToString()); if (personNameDirectory != personFirstInitialDirectory) Directory.Move(personNameDirectory, personFirstInitialDirectory); } files = Directory.GetFiles(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly); if (personKeyFormatted == nameof(IMapLogic.Sorting) && files.Any()) throw new Exception($"Move personKey directories up one from {nameof(IMapLogic.Sorting)} and delete {nameof(IMapLogic.Sorting)} directory!"); if (personKeyFormatted == manualCopyHumanized && files.Any()) throw new Exception($"Move personKey directories up one from {manualCopyHumanized} and delete {manualCopyHumanized} directory!"); if (personKeyFormatted == forceSingleImageHumanized && files.Any()) throw new Exception($"Move personKey directories up one from {forceSingleImageHumanized} and delete {forceSingleImageHumanized} directory!"); if (personKeyFormatted.Length != configuration.PersonBirthdayFormat.Length) continue; foreach (string file in files) { if (file.EndsWith(".lnk") || file.EndsWith(".json")) continue; (id, normalizedPixelPercentage, faces) = IMapping.GetReversedDeterministicHashCodeKey(configuration.LocationDigits, facesFileNameExtension, keyValuePairsAny, keyValuePairs, file); if (id is null || normalizedPixelPercentage is null) continue; if (configuration.MappingMoveUnableToMatch) { if (faces is null) check = false; else { check = false; checks.Clear(); foreach (Face face in faces) { if (face.Mapping is null) throw new NotSupportedException(); checks.Add(face.Mapping.MappingFromLocation.NormalizedPixelPercentage); if (normalizedPixelPercentage.Value != face.Mapping.MappingFromLocation.NormalizedPixelPercentage) continue; check = true; } if (!check) checks.Add(normalizedPixelPercentage.Value); } } results.Add(new(personKeyFormatted, personDisplayDirectoryNames, file, faces)); } personNameLinkDirectories = Directory.GetDirectories(personFirstInitialDirectory, "*", 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); } _ = IPath.DeleteEmptyDirectories(personNameLinkDirectory); } _ = IPath.DeleteEmptyDirectories(personFirstInitialDirectory); } _ = IPath.DeleteEmptyDirectories(yearDirectory); } _ = IPath.DeleteEmptyDirectories(personKeyDirectory); } _ = IPath.DeleteEmptyDirectories(ticksDirectory); _ = IPath.DeleteEmptyDirectories(ticksDirectory); } return results; } private static PersonContainer[] GetDistinctPersonContainers(List personContainers) { List results = new(); List distinctCheck = new(); foreach (PersonContainer personContainer in personContainers) { if (personContainer.Key is null || distinctCheck.Contains(personContainer.Key.Value)) continue; results.Add(personContainer); } return results.ToArray(); } private static void SetKeyValuePairs(Configuration configuration, long ticks, List personContainers, List distinctFilteredFaces, List<(string, string[], int, int)> personKeyFormattedIdThenNormalizedPixelPercentageCollection, List<(string, int, int)> incorrectPersonKeyFormattedIdThenNormalizedPixelPercentageCollection, Dictionary personKeyToPersonContainer, Dictionary> idThenNormalizedPixelPercentageToPersonContainers, List<(string[], PersonContainer)> possiblyNewPersonDisplayDirectoryNamesAndPersonContainer, Dictionary> incorrectIdThenNormalizedPixelPercentageToPersonContainers, Dictionary personKeyToRanges) { string check; const int zero = 0; string rightPadded; PersonBirthday? personBirthday; string newestPersonKeyFormatted; PersonContainer[] distinctPersonContainers; int normalizedPixelPercentageInDecimalForm; Dictionary personKeyFormattedToPersonContainer = new(); Dictionary> personKeyToPersonContainerCollection = new(); Dictionary>> idThenNormalizedPixelPercentageToPersonContainerCollection = new(); Dictionary>> incorrectIdThenNormalizedPixelPercentageToPersonContainerCollection = new(); foreach (PersonContainer personContainer in personContainers) { if (personContainer.Key is null) continue; if (!personKeyToPersonContainerCollection.ContainsKey(personContainer.Key.Value)) personKeyToPersonContainerCollection.Add(personContainer.Key.Value, new()); personKeyToPersonContainerCollection[personContainer.Key.Value].Add(personContainer); newestPersonKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personContainer.Key.Value); if (!personKeyFormattedToPersonContainer.ContainsKey(newestPersonKeyFormatted)) personKeyFormattedToPersonContainer.Add(newestPersonKeyFormatted, personContainer); } foreach (KeyValuePair> keyValuePair in personKeyToPersonContainerCollection) { if (keyValuePair.Value.Count != 1 && (from l in keyValuePair.Value select l.DisplayDirectoryName).Distinct().Count() != 1) throw new NotImplementedException(); personKeyToPersonContainer.Add(keyValuePair.Key, keyValuePair.Value[zero]); } if (personKeyFormattedIdThenNormalizedPixelPercentageCollection.Any()) { string personDisplayDirectory; PersonContainer personContainer; string personDisplayDirectoryName; Dictionary personDisplayDirectoryTo = new(); foreach ((string personKeyFormatted, string[] personDisplayDirectoryNames, int id, int normalizedPixelPercentage) in personKeyFormattedIdThenNormalizedPixelPercentageCollection) { personBirthday = IPersonBirthday.GetPersonBirthday(configuration.PersonBirthdayFormat, personKeyFormatted); if (personBirthday is null) continue; personDisplayDirectoryName = personDisplayDirectoryNames[^1]; personDisplayDirectory = Path.Combine(personDisplayDirectoryNames); if (!personKeyFormattedToPersonContainer.ContainsKey(personKeyFormatted)) { personContainer = new(personBirthday, personDisplayDirectoryName); personKeyFormattedToPersonContainer.Add(personKeyFormatted, personContainer); } if (personDisplayDirectoryName.Length != 1 && personDisplayDirectoryName != configuration.MappingDefaultName && !personDisplayDirectoryTo.ContainsKey(personDisplayDirectory)) personDisplayDirectoryTo.Add(personDisplayDirectory, new(personDisplayDirectoryNames, personKeyFormattedToPersonContainer[personKeyFormatted])); if (!idThenNormalizedPixelPercentageToPersonContainerCollection.ContainsKey(id)) idThenNormalizedPixelPercentageToPersonContainerCollection.Add(id, new()); check = normalizedPixelPercentage.ToString(); if (check.Length == configuration.LocationDigits) { if (!idThenNormalizedPixelPercentageToPersonContainerCollection[id].ContainsKey(normalizedPixelPercentage)) idThenNormalizedPixelPercentageToPersonContainerCollection[id].Add(normalizedPixelPercentage, new()); idThenNormalizedPixelPercentageToPersonContainerCollection[id][normalizedPixelPercentage].Add(personKeyFormattedToPersonContainer[personKeyFormatted]); } else { rightPadded = ILocation.GetRightPadded(configuration.LocationDigits, check); normalizedPixelPercentageInDecimalForm = int.Parse(rightPadded); if (!idThenNormalizedPixelPercentageToPersonContainerCollection[id].ContainsKey(normalizedPixelPercentageInDecimalForm)) idThenNormalizedPixelPercentageToPersonContainerCollection[id].Add(normalizedPixelPercentageInDecimalForm, new()); idThenNormalizedPixelPercentageToPersonContainerCollection[id][normalizedPixelPercentage].Add(personKeyFormattedToPersonContainer[personKeyFormatted]); } } foreach (KeyValuePair keyValuePair in personDisplayDirectoryTo) possiblyNewPersonDisplayDirectoryNamesAndPersonContainer.Add(keyValuePair.Value); } foreach (KeyValuePair>> keyValuePair in idThenNormalizedPixelPercentageToPersonContainerCollection) { idThenNormalizedPixelPercentageToPersonContainers.Add(keyValuePair.Key, new()); foreach (KeyValuePair> innerKeyValuePair in keyValuePair.Value) { distinctPersonContainers = GetDistinctPersonContainers(innerKeyValuePair.Value); idThenNormalizedPixelPercentageToPersonContainers[keyValuePair.Key].Add(innerKeyValuePair.Key, distinctPersonContainers); } }; SetPersonTicks(ticks, distinctFilteredFaces, personKeyToRanges, idThenNormalizedPixelPercentageToPersonContainers); if (incorrectPersonKeyFormattedIdThenNormalizedPixelPercentageCollection.Any()) { PersonContainer personContainer; foreach ((string personKeyFormatted, int id, int normalizedPixelPercentage) in incorrectPersonKeyFormattedIdThenNormalizedPixelPercentageCollection) { personBirthday = IPersonBirthday.GetPersonBirthday(configuration.PersonBirthdayFormat, personKeyFormatted); if (personBirthday is null) continue; if (!incorrectIdThenNormalizedPixelPercentageToPersonContainerCollection.ContainsKey(id)) incorrectIdThenNormalizedPixelPercentageToPersonContainerCollection.Add(id, new()); check = normalizedPixelPercentage.ToString(); if (!personKeyFormattedToPersonContainer.ContainsKey(personKeyFormatted)) { personContainer = new(personBirthday, configuration.MappingDefaultName); personKeyFormattedToPersonContainer.Add(personKeyFormatted, personContainer); } if (check.Length == configuration.LocationDigits) { if (!incorrectIdThenNormalizedPixelPercentageToPersonContainerCollection[id].ContainsKey(normalizedPixelPercentage)) incorrectIdThenNormalizedPixelPercentageToPersonContainerCollection[id].Add(normalizedPixelPercentage, new()); incorrectIdThenNormalizedPixelPercentageToPersonContainerCollection[id][normalizedPixelPercentage].Add(personKeyFormattedToPersonContainer[personKeyFormatted]); } else { rightPadded = ILocation.GetRightPadded(configuration.LocationDigits, check); normalizedPixelPercentageInDecimalForm = int.Parse(rightPadded); if (!incorrectIdThenNormalizedPixelPercentageToPersonContainerCollection[id].ContainsKey(normalizedPixelPercentageInDecimalForm)) incorrectIdThenNormalizedPixelPercentageToPersonContainerCollection[id].Add(normalizedPixelPercentageInDecimalForm, new()); incorrectIdThenNormalizedPixelPercentageToPersonContainerCollection[id][normalizedPixelPercentage].Add(personKeyFormattedToPersonContainer[personKeyFormatted]); } } } foreach (KeyValuePair>> keyValuePair in incorrectIdThenNormalizedPixelPercentageToPersonContainerCollection) { incorrectIdThenNormalizedPixelPercentageToPersonContainers.Add(keyValuePair.Key, new()); foreach (KeyValuePair> innerKeyValuePair in keyValuePair.Value) { distinctPersonContainers = GetDistinctPersonContainers(innerKeyValuePair.Value); incorrectIdThenNormalizedPixelPercentageToPersonContainers[keyValuePair.Key].Add(innerKeyValuePair.Key, distinctPersonContainers); } } } private static string? GetCheckFile(string facesFileNameExtension, string file, int id, int normalizedPixelPercentage) { string? result; string fileName = Path.GetFileName(file); string? directoryName = Path.GetDirectoryName(file); (string? Id, string? NormalizedPixelPercentage, string? ExtensionLowered, bool? Check) segments = IMapping.GetSegments(facesFileNameExtension, fileName); if (string.IsNullOrEmpty(directoryName) || string.IsNullOrEmpty(segments.Id) || string.IsNullOrEmpty(segments.NormalizedPixelPercentage) || string.IsNullOrEmpty(segments.ExtensionLowered)) result = null; else result = Path.Combine(directoryName, $"{IMapping.GetDeterministicHashCodeKey(id, normalizedPixelPercentage)}{segments.ExtensionLowered}.json"); return result; } private static bool Valid(string checkFile, List faces) { bool result = false; string json; foreach (Face face in faces) { if (face.FaceEncoding is null) throw new NotSupportedException(); if (faces.Count != 1) break; result = true; if (File.Exists(checkFile)) continue; json = JsonSerializer.Serialize(face.FaceEncoding); if (IPath.WriteAllText(checkFile, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null)) continue; } return result; } private static void MoveUnableToMatch(Configuration configuration, string eDistanceContentDirectory, string file, string jsonFile) { bool result; string? fileName = Path.GetFileName(file); string? jsonFileName = Path.GetFileName(jsonFile); if (fileName is null || jsonFileName is null) result = false; else { string? directoryName = Path.GetDirectoryName(jsonFile); string? jsonDirectoryName = Path.GetDirectoryName(jsonFile); if (string.IsNullOrEmpty(directoryName) || string.IsNullOrEmpty(directoryName) || directoryName != jsonDirectoryName || !directoryName.Contains(eDistanceContentDirectory)) result = false; else { List directoryNames = new(); string? checkDirectoryName = directoryName; for (int i = 0; i < int.MaxValue; i++) { if (string.IsNullOrEmpty(checkDirectoryName)) continue; directoryNames.Add(Path.GetFileName(checkDirectoryName)); checkDirectoryName = Path.GetDirectoryName(checkDirectoryName); if (string.IsNullOrEmpty(checkDirectoryName)) continue; if (checkDirectoryName == eDistanceContentDirectory) break; } if (string.IsNullOrEmpty(checkDirectoryName) || !directoryNames.Any() || !long.TryParse(directoryNames[^1][1..^1], out long directoryTicks)) result = false; else { bool jsonFileExists = File.Exists(jsonFile); if (!jsonFileExists) checkDirectoryName = Path.Combine(checkDirectoryName, $"({directoryTicks}.00)"); else checkDirectoryName = Path.Combine(checkDirectoryName, $"({directoryTicks}{configuration.FaceDistanceTolerance.ToString()[1..]})"); for (int i = directoryNames.Count - 1 - 1; i > -1; i--) checkDirectoryName = Path.Combine(checkDirectoryName, directoryNames[i]); if (!Directory.Exists(checkDirectoryName)) _ = Directory.CreateDirectory(checkDirectoryName); File.Move(file, Path.Combine(checkDirectoryName, fileName)); if (jsonFileExists) File.Move(jsonFile, Path.Combine(checkDirectoryName, jsonFileName)); result = true; } } } if (result) { } } private static int SetCollectionsAndGetUnableToMatchCount(Configuration configuration, string facesFileNameExtension, long ticks, string eDistanceContentDirectory, Shared.Models.Methods.IFaceDistance? distance, Dictionary personKeyFormattedToNewestPersonKeyFormatted, List<(string, string[], int, int)> personKeyFormattedIdThenNormalizedPixelPercentageCollection, List<(string, int, int)> incorrectPersonKeyFormattedIdThenNormalizedPixelPercentageCollection, List<(string, string[], string, List?)> collection) { int? id; int result = 0; string? checkFile; List checkFaces = new(); int? normalizedPixelPercentage; string newestPersonKeyFormatted; string personDisplayDirectoryName; int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string message = $") {collection.Count:000} join from ticks Director(ies) - B - {totalSeconds} total second(s)"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using ProgressBar progressBar = new(collection.Count, message, options); foreach ((string personKeyFormatted, string[] personDisplayDirectoryNames, string file, List? faces) in collection) { progressBar.Tick(); (id, normalizedPixelPercentage, _) = IMapping.GetReversedDeterministicHashCodeKey(configuration.LocationDigits, facesFileNameExtension, file); if (id is null || normalizedPixelPercentage is null) { result++; continue; } checkFile = GetCheckFile(facesFileNameExtension, file, id.Value, normalizedPixelPercentage.Value); if (string.IsNullOrEmpty(checkFile)) throw new NotSupportedException(); if (faces is null) { result++; continue; } checkFaces.Clear(); foreach (Face face in faces) { if (face.Mapping is null) throw new NotSupportedException(); if (normalizedPixelPercentage.Value != face.Mapping.MappingFromLocation.NormalizedPixelPercentage) continue; checkFaces.Add(face); } if (checkFaces.Count != 1 && distance is not null && File.Exists(checkFile)) { checkFaces.Clear(); checkFaces.AddRange(distance.GetMatchingFaces(configuration.FaceDistanceTolerance, checkFile, faces)); } if (!checkFaces.Any()) { result++; if (configuration.MappingMoveUnableToMatch) MoveUnableToMatch(configuration, eDistanceContentDirectory, file, checkFile); continue; } if (checkFaces.Count != 1) { result++; if (configuration.MappingMoveUnableToMatch) MoveUnableToMatch(configuration, eDistanceContentDirectory, file, checkFile); continue; } if (!Valid(checkFile, checkFaces)) { result++; if (configuration.MappingMoveUnableToMatch) MoveUnableToMatch(configuration, eDistanceContentDirectory, file, checkFile); continue; } if (!personKeyFormattedToNewestPersonKeyFormatted.ContainsKey(personKeyFormatted)) newestPersonKeyFormatted = personKeyFormatted; else newestPersonKeyFormatted = personKeyFormattedToNewestPersonKeyFormatted[personKeyFormatted]; personDisplayDirectoryName = personDisplayDirectoryNames[^1]; if (!string.IsNullOrEmpty(personDisplayDirectoryName) && personDisplayDirectoryName[0] != '!') personKeyFormattedIdThenNormalizedPixelPercentageCollection.Add(new(newestPersonKeyFormatted, personDisplayDirectoryNames, id.Value, normalizedPixelPercentage.Value)); else incorrectPersonKeyFormattedIdThenNormalizedPixelPercentageCollection.Add(new(newestPersonKeyFormatted, id.Value, normalizedPixelPercentage.Value)); } return result; } private static double GetStandardDeviation(IEnumerable values, double average) { double result = 0; if (!values.Any()) throw new Exception("Collection must have at least one value!"); double sum = values.Sum(l => (l - average) * (l - average)); result = Math.Sqrt(sum / values.Count()); return result; } private static void SetPersonKeysRanges(long ticks, Dictionary> personTicks, Dictionary personKeyToRanges) { long lcl; long ucl; long maximum; long minimum; double average; long[] collection; double standardDeviation; foreach (KeyValuePair> keyValuePair in personTicks) { minimum = keyValuePair.Value.Min(); if (keyValuePair.Value.Count < 3) { maximum = keyValuePair.Value.Max(); personKeyToRanges.Add(keyValuePair.Key, new(new DateTime(minimum).AddYears(-1).Ticks, minimum, maximum, new DateTime(maximum).AddYears(1).Ticks)); } else { collection = (from l in keyValuePair.Value select l - minimum).ToArray(); maximum = collection.Max() + minimum; average = (collection.Sum() / collection.Length) + minimum; standardDeviation = GetStandardDeviation(collection, average); ucl = (long)(average + (standardDeviation * IMapLogic.Sigma)); lcl = (long)(average - (standardDeviation * IMapLogic.Sigma)); if (lcl < 0) lcl = 0; if (ucl > 0) ucl = ticks; personKeyToRanges.Add(keyValuePair.Key, new(lcl, minimum, maximum, ucl)); } } } private static void SetPersonTicks(long ticks, List distinctFilteredFaces, Dictionary personKeyToRanges, Dictionary> idThenNormalizedPixelPercentageToPersonContainers) { PersonContainer[] personContainers; Dictionary keyValuePairs; Dictionary> personTicks = new(); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string message = $") {distinctFilteredFaces.Count:000} Set Person Ticks - {totalSeconds} total second(s)"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using ProgressBar progressBar = new(distinctFilteredFaces.Count, message, options); foreach (Face face in distinctFilteredFaces) { progressBar.Tick(); if (face.Mapping is null) throw new NotSupportedException(); if (!idThenNormalizedPixelPercentageToPersonContainers.ContainsKey(face.Mapping.MappingFromItem.Id)) continue; keyValuePairs = idThenNormalizedPixelPercentageToPersonContainers[face.Mapping.MappingFromItem.Id]; if (!keyValuePairs.ContainsKey(face.Mapping.MappingFromLocation.NormalizedPixelPercentage)) continue; personContainers = keyValuePairs[face.Mapping.MappingFromLocation.NormalizedPixelPercentage]; foreach (PersonContainer personContainer in personContainers) { if (personContainer.Key is null) continue; if (!personTicks.ContainsKey(personContainer.Key.Value)) personTicks.Add(personContainer.Key.Value, new()); personTicks[personContainer.Key.Value].Add(face.Mapping.MappingFromItem.MinimumDateTime.Ticks); } } SetPersonKeysRanges(ticks, personTicks, personKeyToRanges); } private static List GetNotMappedPersonContainers(Configuration configuration, List personContainers, long[] personKeyCollection) { List results = new(); List notMappedAndNotNamedPersonKeys = new(); List notMappedAndWithNamedPersonKeys = new(); foreach (PersonContainer personContainer in personContainers) { if (personContainer.Person is null || personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any()) continue; if (personKeyCollection.Contains(personContainer.Key.Value)) continue; else if (string.IsNullOrEmpty(personContainer.DisplayDirectoryName) || personContainer.DisplayDirectoryName == configuration.MappingDefaultName) notMappedAndNotNamedPersonKeys.Add(personContainer); else notMappedAndWithNamedPersonKeys.Add(personContainer); } results.AddRange(from l in notMappedAndNotNamedPersonKeys orderby l.Key is not null, l.Key select l); results.AddRange(from l in notMappedAndWithNamedPersonKeys orderby l.Key is not null, l.Key select l); return results; } private static void AppendToSkipCollection(Dictionary> skipCollection, Dictionary> idThenNormalizedPixelPercentageToPersonContainers, Dictionary> incorrectIdThenNormalizedPixelPercentageToPersonContainers) { foreach (KeyValuePair> keyValuePair in incorrectIdThenNormalizedPixelPercentageToPersonContainers) { if (!skipCollection.ContainsKey(keyValuePair.Key)) skipCollection.Add(keyValuePair.Key, new()); if (idThenNormalizedPixelPercentageToPersonContainers.ContainsKey(keyValuePair.Key)) { if (idThenNormalizedPixelPercentageToPersonContainers[keyValuePair.Key].ContainsKey(keyValuePair.Value.ElementAt(0).Key)) continue; } skipCollection[keyValuePair.Key].AddRange(from l in keyValuePair.Value.Keys select l); } } private static void SetPersonKeyToPersonContainer(Configuration configuration, List personContainers, long[] personKeyCollection, Dictionary personKeyToPersonContainer) { string personDisplayDirectoryName; foreach (PersonContainer personContainer in personContainers) { if (personContainer.Key is null || !personKeyCollection.Contains(personContainer.Key.Value)) continue; if (personKeyToPersonContainer.ContainsKey(personContainer.Key.Value)) { personDisplayDirectoryName = personKeyToPersonContainer[personContainer.Key.Value].DisplayDirectoryName; if (string.IsNullOrEmpty(personDisplayDirectoryName)) throw new NotSupportedException(); if (personDisplayDirectoryName == personContainer.DisplayDirectoryName || (personDisplayDirectoryName[0] == personContainer.DisplayDirectoryName[0] && (personDisplayDirectoryName.Length == 1 || personContainer.DisplayDirectoryName.Length == 1))) continue; throw new NotImplementedException(); } personKeyToPersonContainer.Add(personContainer.Key.Value, personContainer); } if (personKeyCollection.Any()) { int? approximateYears = null; PersonBirthday? personBirthday; PersonContainer personContainer; string displayDirectoryName = configuration.MappingDefaultName; foreach (long personKey in personKeyCollection) { if (personKeyToPersonContainer.ContainsKey(personKey)) continue; personBirthday = IPersonBirthday.GetPersonBirthday(personKey); personContainer = new(approximateYears, personBirthday, displayDirectoryName, personKey); personKeyToPersonContainer.Add(personKey, personContainer); } } } internal static void Set(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration? configuration, string facesFileNameExtension, long ticks, List personContainers, string eDistanceContentDirectory, List distinctFilteredFaces, Shared.Models.Methods.IFaceDistance faceDistance, Dictionary personKeyToPersonContainer, Dictionary personKeyToRanges, List notMappedPersonContainers, Dictionary> skipCollection, Dictionary> idThenNormalizedPixelPercentageToPersonContainers) { if (configuration is null) throw new NullReferenceException(nameof(configuration)); List personKeys = new(); List nullablePersonKeyCollection = new(); List personKeyFormattedCollection = new(); Dictionary> keyValuePairs = new(); Dictionary personKeyFormattedToNewestPersonKeyFormatted = new(); List<(string[], PersonContainer)> possiblyNewPersonDisplayDirectoryNamesAndPersonContainer = new(); List<(string, string[], int, int)> personKeyFormattedIdThenNormalizedPixelPercentageCollection = new(); List<(string, int, int)> incorrectPersonKeyFormattedIdThenNormalizedPixelPercentageCollection = new(); Dictionary> incorrectIdThenNormalizedPixelPercentageToPersonContainers = new(); SetPersonCollections( configuration, facesFileNameExtension, personContainers, personKeys, personKeyFormattedToNewestPersonKeyFormatted, personKeyFormattedCollection, skipCollection); personContainers.AddRange(AddToPersonKeysThenGetNonSpecificPeopleCollection(configuration, personKeys)); foreach (Face face in distinctFilteredFaces) { if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) throw new NotSupportedException(); if (face.Mapping is null) throw new NotSupportedException(); if (!keyValuePairs.ContainsKey(face.Mapping.MappingFromItem.Id)) keyValuePairs.Add(face.Mapping.MappingFromItem.Id, new()); keyValuePairs[face.Mapping.MappingFromItem.Id].Add(face); } List<(string, string[], string, List?)> collection = DeleteEmptyDirectoriesAndGetCollection(configuration, facesFileNameExtension, ticks, eDistanceContentDirectory, personKeyFormattedCollection, keyValuePairs); int unableToMatchCount = SetCollectionsAndGetUnableToMatchCount( configuration, facesFileNameExtension, ticks, eDistanceContentDirectory, faceDistance, personKeyFormattedToNewestPersonKeyFormatted, personKeyFormattedIdThenNormalizedPixelPercentageCollection, incorrectPersonKeyFormattedIdThenNormalizedPixelPercentageCollection, collection); SetKeyValuePairs( configuration, ticks, personContainers, distinctFilteredFaces, personKeyFormattedIdThenNormalizedPixelPercentageCollection, incorrectPersonKeyFormattedIdThenNormalizedPixelPercentageCollection, personKeyToPersonContainer, idThenNormalizedPixelPercentageToPersonContainers, possiblyNewPersonDisplayDirectoryNamesAndPersonContainer, incorrectIdThenNormalizedPixelPercentageToPersonContainers, personKeyToRanges); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); string message = $") {collection.Count:000} message from ticks Director(ies) - C - {unableToMatchCount} Unable To Match Count / {collection.Count} Collection - {totalSeconds} total second(s)"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using (ProgressBar progressBar = new(collection.Count, message, options)) { foreach (KeyValuePair> keyValuePair in idThenNormalizedPixelPercentageToPersonContainers) { progressBar.Tick(); foreach (KeyValuePair keyValue in keyValuePair.Value) nullablePersonKeyCollection.AddRange(from l in keyValue.Value select l.Key); } } long[] personKeyCollection = (from l in nullablePersonKeyCollection where l is not null select l.Value).Distinct().ToArray(); SetPersonKeyToPersonContainer(configuration, personContainers, personKeyCollection, personKeyToPersonContainer); notMappedPersonContainers.AddRange(GetNotMappedPersonContainers(configuration, personContainers, personKeyCollection)); AppendToSkipCollection( skipCollection, idThenNormalizedPixelPercentageToPersonContainers, incorrectIdThenNormalizedPixelPercentageToPersonContainers); if (possiblyNewPersonDisplayDirectoryNamesAndPersonContainer.Any()) faceDistance.SavePossiblyNewPersonContainers( propertyConfiguration, configuration.PersonBirthdayFormat, facesFileNameExtension, personKeyToPersonContainer, possiblyNewPersonDisplayDirectoryNamesAndPersonContainer); } }