Moved more to Map library

This commit is contained in:
Mike Phares 2022-08-29 08:45:01 -07:00
parent 674555b4fc
commit c1d30b5bbc
46 changed files with 1631 additions and 697 deletions

View File

@ -54,7 +54,8 @@ public class Compare
bool reverse = false; bool reverse = false;
string outputExtension = ".jpg"; string outputExtension = ".jpg";
PredictorModel? predictorModel = null; 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); A_Property propertyLogic = GetPropertyLogic(reverse, model, outputExtension, predictorModel, mapLogic);
foreach (string spelling in configuration.Spelling) foreach (string spelling in configuration.Spelling)
{ {

View File

@ -234,6 +234,7 @@ public class DlibDotNet
sourceDirectoryNames = Array.Empty<string>(); sourceDirectoryNames = Array.Empty<string>();
else else
{ {
string? century;
string argZero = Path.GetFullPath(args[0]); string argZero = Path.GetFullPath(args[0]);
sourceDirectoryNames = argZero.Split(Path.DirectorySeparatorChar); sourceDirectoryNames = argZero.Split(Path.DirectorySeparatorChar);
if (!argZero.StartsWith(propertyConfiguration.RootDirectory)) if (!argZero.StartsWith(propertyConfiguration.RootDirectory))
@ -243,7 +244,8 @@ public class DlibDotNet
if (!configuration.MixedYearRelativePaths.Contains(sourceDirectoryNames[0])) if (!configuration.MixedYearRelativePaths.Contains(sourceDirectoryNames[0]))
{ {
string[] segments = sourceDirectoryNames[0].Split(' '); 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!"); 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}"; 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)) using (ProgressBar progressBar = new(filteredItems.Length, message, options))
{ {
_ = Parallel.For(0, filteredItems.Length, parallelOptions, i => _ = Parallel.For(0, filteredItems.Length, parallelOptions, (i, state) =>
{ {
try 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) private void Search(Property.Models.Configuration configuration, bool reverse, Model? model, PredictorModel? predictorModel, string argZero, Person[] people, bool isSilent)
{ {
if (_Log is null) if (_Log is null)
@ -653,8 +685,8 @@ public class DlibDotNet
string eResultsFullGroupDirectory; string eResultsFullGroupDirectory;
string zResultsFullGroupDirectory; string zResultsFullGroupDirectory;
string d2ResultsFullGroupDirectory; string d2ResultsFullGroupDirectory;
Dictionary<string, List<Person>> peopleCollection = A2_People.Convert(people); Dictionary<string, Person> personKeyValuePairs = A2_People.Convert(people);
Map.Models.MapLogic mapLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration); 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); A_Property propertyLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FilenameExtension, reverse, model, predictorModel, mapLogic.IndicesFromNew, mapLogic.KeyValuePairs);
if (string.IsNullOrEmpty(configuration.RootDirectory)) if (string.IsNullOrEmpty(configuration.RootDirectory))
containers = A_Property.Get(configuration, propertyLogic); containers = A_Property.Get(configuration, propertyLogic);
@ -669,30 +701,35 @@ public class DlibDotNet
mapLogic.UseKeyValuePairsSaveFaceEncoding(containers); mapLogic.UseKeyValuePairsSaveFaceEncoding(containers);
foreach (Container container in containers) foreach (Container container in containers)
{ {
mapLogic.AddToNamed(container.Items); mapLogic.AddToMapping(container.Items);
if (_Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution)) 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(); mapLogic.SaveAllCollection();
if (_Configuration.SaveResizedSubfiles) if (_Configuration.SaveResizedSubfiles)
{ {
string dFacesContentDirectory; string dFacesContentDirectory;
string eDistanceContentDirectory; string zPropertyHolderContentDirectory;
string eDistanceCollectionDirectory;
string zPropertyHolderSingletonDirectory; string zPropertyHolderSingletonDirectory;
string zPropertyHolderCollectionDirectory;
dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, "()"); dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, "()");
eDistanceContentDirectory = Path.Combine(eResultsFullGroupDirectory, $"({ticks})");
zPropertyHolderSingletonDirectory = Path.Combine(zResultsFullGroupDirectory, "{}"); zPropertyHolderSingletonDirectory = Path.Combine(zResultsFullGroupDirectory, "{}");
eDistanceCollectionDirectory = Path.Combine(eResultsFullGroupDirectory, $"[{ticks}]"); zPropertyHolderContentDirectory = Path.Combine(zResultsFullGroupDirectory, $"({ticks})");
List<(DateTime, bool?, PersonBirthday, FaceRecognitionDotNet.FaceEncoding[])> collection; zPropertyHolderCollectionDirectory = Path.Combine(zResultsFullGroupDirectory, $"[{ticks}]");
collection = E_Distance.ParallelWork(_AppSettings.MaxDegreeOfParallelism, argZero, mapLogic, containers); Dictionary<string, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>> keyValuePairs = _Distance.ParallelWork(_AppSettings.MaxDegreeOfParallelism, _Configuration.IgnoreRelativePaths, argZero, ticks, containers);
_ = LogDeltaInSeconds(ticks, nameof(E_Distance.ParallelWork)); _ = 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); E_Distance.SavePropertyHolders(argZero, containers, zPropertyHolderSingletonDirectory);
_ = LogDeltaInSeconds(ticks, nameof(E_Distance.SavePropertyHolders)); _ = 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()) if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Any())
break; break;

View File

@ -73,16 +73,16 @@ internal class A2_People
return results.ToArray(); 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; string personKey;
foreach (Person person in people) foreach (Person person in people)
{ {
personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(person.Birthday); personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(person.Birthday);
if (!results.ContainsKey(personKey)) if (results.ContainsKey(personKey))
results.Add(personKey, new List<Person>()); break;
results[personKey].Add(person); results.Add(personKey, person);
} }
return results; return results;
} }

View File

@ -153,7 +153,7 @@ internal class D2_FaceParts
collection.Add(new(face, string.Empty, string.Empty)); collection.Add(new(face, string.Empty, string.Empty));
continue; 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}")); fileInfo = new FileInfo(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FilenameExtension}"));
if (!fileInfo.Exists) if (!fileInfo.Exists)
{ {

View File

@ -2,14 +2,12 @@ using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions;
using View_by_Distance.FaceRecognitionDotNet; using View_by_Distance.FaceRecognitionDotNet;
using View_by_Distance.Metadata.Models; using View_by_Distance.Metadata.Models;
using View_by_Distance.Property.Models; using View_by_Distance.Property.Models;
using View_by_Distance.Resize.Models; using View_by_Distance.Resize.Models;
using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Stateless; using View_by_Distance.Shared.Models.Stateless;
using WindowsShortcutFactory;
namespace View_by_Distance.Instance.Models; namespace View_by_Distance.Instance.Models;
@ -346,7 +344,7 @@ public class D_Face
collection.Add(new(face, string.Empty)); collection.Add(new(face, string.Empty));
continue; 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}")); fileInfo = new FileInfo(Path.Combine(facesDirectory, $"{deterministicHashCodeKey}{item.ImageFileHolder.ExtensionLowered}{_FilenameExtension}"));
if (!fileInfo.Exists) if (!fileInfo.Exists)
{ {
@ -368,60 +366,6 @@ public class D_Face
SaveFaces(item.ResizedFileHolder, collection); 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) private static bool HasLeftAndRight(Dictionary<string, FacePoint[]> faceParts)
{ {
bool result = true; bool result = true;

View File

@ -1,13 +1,10 @@
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions;
using View_by_Distance.FaceRecognitionDotNet; using View_by_Distance.FaceRecognitionDotNet;
using View_by_Distance.Metadata.Models; using View_by_Distance.Metadata.Models;
using View_by_Distance.Property.Models; using View_by_Distance.Property.Models;
using View_by_Distance.Resize.Models; using View_by_Distance.Resize.Models;
using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless; using View_by_Distance.Shared.Models.Stateless;
using WindowsShortcutFactory;
namespace View_by_Distance.Instance.Models; namespace View_by_Distance.Instance.Models;
@ -424,6 +421,152 @@ internal class E_Distance
return result; 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) private static FaceRecognitionDotNet.FaceEncoding? GetFaceEncoding(Face face)
{ {
FaceRecognitionDotNet.FaceEncoding? result; FaceRecognitionDotNet.FaceEncoding? result;
@ -434,24 +577,18 @@ internal class E_Distance
return result; return result;
} }
private static (int index, double sum) GetIndexAndSum(int i, List<FaceRecognitionDotNet.FaceEncoding> results) private static List<FaceRecognitionDotNet.FaceEncoding> GetFaceEncodingsOnly(int maxDegreeOfParallelism, List<MappingContainer> collection)
{
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)
{ {
List<FaceRecognitionDotNet.FaceEncoding> results; List<FaceRecognitionDotNet.FaceEncoding> results;
if (maxDegreeOfParallelism == 1) if (maxDegreeOfParallelism == 1)
{ {
results = new(); results = new();
FaceRecognitionDotNet.FaceEncoding faceEncoding; 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; continue;
faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding); faceEncoding = FaceRecognition.LoadFaceEncoding(mappingContainer.Face.FaceEncoding.RawEncoding);
results.Add(faceEncoding); results.Add(faceEncoding);
} }
} }
@ -459,9 +596,12 @@ internal class E_Distance
{ {
results = new(); results = new();
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; 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) if (faceEncoding is not null)
{ {
lock (results) 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; 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(); if (_Log is null)
const int zero = 0; 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; 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); collection = new();
results.Add(new(keyValuePair.Value[zero].MinimumDateTime, keyValuePair.Value[zero].IsWrongYear, keyValuePair.Value[zero].PersonBirthday, faceEncodings.ToArray())); faceEncodings = GetFaceEncodingsOnly(maxDegreeOfParallelism, keyValuePair.Value);
for (int i = 0; i < faceEncodings.Count; i++)
{
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}");
} }
return results; 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) internal Dictionary<string, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>> ParallelWork(int maxDegreeOfParallelism, string[] ignoreRelativePaths, string argZero, long ticks, List<Container> containers)
{ {
Closest? result; Dictionary<string, List<(FaceRecognitionDotNet.FaceEncoding, MappingContainer)>> results;
if (isWrongYear.HasValue && !isWrongYear.Value && minimumDateTime < tuple.PersonBirthday.Value) Dictionary<string, List<MappingContainer>> keyValuePairs = Map.Models.Stateless.IMapLogic.GetKeyValuePairs(ignoreRelativePaths, argZero, containers);
result = null; results = GetThreeSigmaFaceEncodings(maxDegreeOfParallelism, ticks, keyValuePairs);
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)
{
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);
}
}
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; return results;
} }
private static void AddClosest(int maxDegreeOfParallelism, string argZero, Map.Models.MapLogic mapLogic, List<Container> containers, List<(DateTime, bool?, PersonBirthday, FaceRecognitionDotNet.FaceEncoding[])> collection) 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)
{ {
string key; if (_Log is null)
throw new NullReferenceException(nameof(_Log));
Face face; Face face;
Closest closest; string message;
string personKey; int totalSeconds;
bool? itemIsWrongYear;
Closest[] closestCollection;
DateTime? itemMinimumDateTime;
double deterministicHashCodeKey; double deterministicHashCodeKey;
Dictionary<string, int> results = new(); DateTime dateTime = DateTime.Now;
List<FaceDistance> faceDistances;
int containersCount = containers.Count;
foreach (Container container in containers) foreach (Container container in containers)
{ {
if (!container.Items.Any()) if (!container.Items.Any())
@ -607,54 +679,28 @@ internal class E_Distance
continue; continue;
foreach (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?.Id is null)
continue; 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++) for (int i = 0; i < item.Faces.Count; i++)
{ {
face = item.Faces[i]; face = item.Faces[i];
closest = new(face.Location?.NormalizedPixelPercentage, itemMinimumDateTime.Value, itemIsWrongYear); face.FaceDistances.Clear();
item.Closest.Add(closest);
if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null)
continue; continue;
deterministicHashCodeKey = Shared.Models.Stateless.Methods.INamed.GetDeterministicHashCodeKey(item, face); if ((from l in item.Mapping where l.NormalizedPixelPercentage.HasValue && l.NormalizedPixelPercentage.Value == face.Location.NormalizedPixelPercentage.Value select true).Any())
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; continue;
personKey = Shared.Models.Stateless.Methods.IPersonBirthday.GetFormatted(closest.PersonBirthday); deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face);
if (mapLogic.IsIncorrect(deterministicHashCodeKey, personKey)) if (mapLogic.Skip(deterministicHashCodeKey))
continue; continue;
key = Map.Models.MapLogic.GetKey(closest.MinimumDateTime, closest.IsWrongYear, closest.PersonBirthday); faceDistances = GetFaceDistanceCollection(maxDegreeOfParallelism, collection, face);
if (!results.ContainsKey(key)) face.FaceDistances.AddRange(faceDistances);
results.Add(key, 0);
else if (results[key] > Shared.Models.Stateless.IClosest.MaximumPer)
continue;
results[key] += 1;
item.Closest[0] = closest;
break;
} }
} }
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) public static void SavePropertyHolders(string argZero, List<Container> containers, string zPropertyHolderSingletonDirectory)
{ {
@ -672,8 +718,6 @@ internal class E_Distance
{ {
if (item.ImageFileHolder is null || item.Property is null || !item.Faces.Any() || !item.Closest.Any()) if (item.ImageFileHolder is null || item.Property is null || !item.Faces.Any() || !item.Closest.Any())
continue; continue;
if (!(from l in item.Closest where l.Average.HasValue select true).Any())
continue;
json = JsonSerializer.Serialize(item, jsonSerializerOptions); json = JsonSerializer.Serialize(item, jsonSerializerOptions);
fileInfo = new(string.Concat(zPropertyHolderSingletonDirectory, item.RelativePath, ".json")); fileInfo = new(string.Concat(zPropertyHolderSingletonDirectory, item.RelativePath, ".json"));
if (fileInfo.Directory is null) 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)
{ }
}
}
} }

View File

@ -38,6 +38,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Text.Json" Version="6.0.0" /> <PackageReference Include="System.Text.Json" Version="6.0.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

@ -1,28 +1,34 @@
using System.Diagnostics; using System.Diagnostics;
using System.Text.Json; using System.Text.Json;
using View_by_Distance.Property.Models; 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; namespace View_by_Distance.Map.Models;
public class MapLogic public class MapLogic
{ {
protected readonly List<double> _SkipCollection;
protected readonly List<(int, string[])> _AllCollection; protected readonly List<(int, string[])> _AllCollection;
protected readonly Dictionary<int, int[]> _KeyValuePairs; protected readonly Dictionary<int, int[]> _KeyValuePairs;
protected readonly Dictionary<int, int[]> _IndicesFromNew; 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[]> _SixCharacterNamedFaceInfo;
protected readonly Dictionary<int, string[]> _DeterministicHashCodeUnknownFaceKeyValuePairs;
protected readonly Dictionary<double, string[]> _DeterministicHashCodeKeyValuePairs; protected readonly Dictionary<double, string[]> _DeterministicHashCodeKeyValuePairs;
protected readonly Dictionary<int, string[]> _DeterministicHashCodeUnknownFaceKeyValuePairs;
protected readonly Dictionary<double, string[]> _IncorrectDeterministicHashCodeKeyValuePairs; 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[]> KeyValuePairs => _KeyValuePairs;
public Dictionary<int, int[]> IndicesFromNew => _IndicesFromNew; public Dictionary<int, int[]> IndicesFromNew => _IndicesFromNew;
private readonly Serilog.ILogger? _Log; private readonly Serilog.ILogger? _Log;
private readonly string _OutputExtension;
private readonly Configuration _Configuration; private readonly Configuration _Configuration;
public MapLogic(int maxDegreeOfParallelism, Configuration configuration) public MapLogic(int maxDegreeOfParallelism, Configuration configuration, string outputExtension, Dictionary<string, Person> personKeyValuePairs)
{ {
_AllCollection = new(); _AllCollection = new();
_Configuration = configuration; _Configuration = configuration;
@ -33,17 +39,20 @@ public class MapLogic
string json; string json;
string[] files; string[] files;
string fullPath; string fullPath;
_OutputExtension = outputExtension;
List<double> skipCollection = new();
Dictionary<int, int[]>? keyValuePairs; Dictionary<int, int[]>? keyValuePairs;
string deterministicHashCodeRootDirectory;
List<KeyValuePair<int, int[]>>? collection; List<KeyValuePair<int, int[]>>? collection;
string deterministicHashCodeContentDirectory;
Dictionary<int, int[]> indicesFromNew = new(); Dictionary<int, int[]> indicesFromNew = new();
Dictionary<int, string[]>? sixCharacterNamedFaceInfo; Dictionary<int, string[]>? sixCharacterNamedFaceInfo;
Dictionary<double, string[]> deterministicHashCodeKeyValuePairs = new(); Dictionary<double, string[]> deterministicHashCodeKeyValuePairs = new();
Dictionary<double, string[]> incorrectDeterministicHashCodeKeyValuePairs = new(); Dictionary<double, string[]> incorrectDeterministicHashCodeKeyValuePairs = new();
string? rootDirectoryParent = Path.GetDirectoryName(configuration.RootDirectory); string? rootDirectoryParent = Path.GetDirectoryName(configuration.RootDirectory);
Dictionary<string, (string, int?, string, PersonBirthday[])> peopleKeyValuePairs = new();
if (string.IsNullOrEmpty(rootDirectoryParent)) if (string.IsNullOrEmpty(rootDirectoryParent))
throw new NullReferenceException(nameof(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) if (files.Length != 1)
deterministicHashCodeUnknownFaceKeyValuePairs = new(); deterministicHashCodeUnknownFaceKeyValuePairs = new();
else else
@ -53,15 +62,11 @@ public class MapLogic
if (deterministicHashCodeUnknownFaceKeyValuePairs is null) if (deterministicHashCodeUnknownFaceKeyValuePairs is null)
throw new NullReferenceException(nameof(deterministicHashCodeUnknownFaceKeyValuePairs)); throw new NullReferenceException(nameof(deterministicHashCodeUnknownFaceKeyValuePairs));
} }
string[] directories = Directory.GetDirectories(rootDirectoryParent, "*DeterministicHashCode*", SearchOption.TopDirectoryOnly); string[] directories = Directory.GetDirectories(rootDirectoryParent, "DeterministicHashCode", SearchOption.TopDirectoryOnly);
if (!directories.Any()) if (!directories.Any())
deterministicHashCodeRootDirectory = string.Empty; deterministicHashCodeContentDirectory = string.Empty;
else else
{ deterministicHashCodeContentDirectory = Stateless.ByDeterministicHashCode.SetByRef(_OutputExtension, personKeyValuePairs, skipCollection, peopleKeyValuePairs, deterministicHashCodeUnknownFaceKeyValuePairs, deterministicHashCodeKeyValuePairs, incorrectDeterministicHashCodeKeyValuePairs, directories[0]);
Dictionary<int, List<Shared.Models.Face>> faces = new();
deterministicHashCodeRootDirectory = directories[0];
SetKeyValuePairs(deterministicHashCodeRootDirectory, deterministicHashCodeKeyValuePairs, incorrectDeterministicHashCodeKeyValuePairs, faces);
}
if (!deterministicHashCodeUnknownFaceKeyValuePairs.Any()) if (!deterministicHashCodeUnknownFaceKeyValuePairs.Any())
sixCharacterNamedFaceInfo = new(); sixCharacterNamedFaceInfo = new();
else else
@ -77,7 +82,7 @@ public class MapLogic
throw new NullReferenceException(nameof(sixCharacterNamedFaceInfo)); 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) if (files.Length != 1)
keyValuePairs = new(); keyValuePairs = new();
else else
@ -107,116 +112,80 @@ public class MapLogic
} }
_KeyValuePairs = keyValuePairs; _KeyValuePairs = keyValuePairs;
_IndicesFromNew = indicesFromNew; _IndicesFromNew = indicesFromNew;
_SkipCollection = skipCollection;
_PeopleKeyValuePairs = peopleKeyValuePairs;
_SixCharacterNamedFaceInfo = sixCharacterNamedFaceInfo; _SixCharacterNamedFaceInfo = sixCharacterNamedFaceInfo;
_DeterministicHashCodeRootDirectory = deterministicHashCodeRootDirectory;
_DeterministicHashCodeKeyValuePairs = deterministicHashCodeKeyValuePairs; _DeterministicHashCodeKeyValuePairs = deterministicHashCodeKeyValuePairs;
_DeterministicHashCodeContentDirectory = deterministicHashCodeContentDirectory;
_IncorrectDeterministicHashCodeKeyValuePairs = incorrectDeterministicHashCodeKeyValuePairs; _IncorrectDeterministicHashCodeKeyValuePairs = incorrectDeterministicHashCodeKeyValuePairs;
_DeterministicHashCodeUnknownFaceKeyValuePairs = deterministicHashCodeUnknownFaceKeyValuePairs; _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 fileName;
string personKey; string fullName;
string[] yearDirectories; DateTime? minimumDateTime;
string ticksDirectoryName; WindowsShortcut windowsShortcut;
string[] personKeyDirectories; (string DisplayDirectoryName, int? ApproximateYears, string Key, PersonBirthday[] _) person;
string[] personNameDirectories; string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, $"({ticks})");
string[] personNameLinkDirectories; List<(Item, (string, Face?, (string, string, string, string))[])> collections = GetCollection(items, dFacesContentDirectory);
double? reversedDeterministicHashCodeKey; foreach ((Item item, (string personKey, Face? _, (string, string, string, string))[] collection) in collections)
bool keyValuePairsAny = keyValuePairs.Any();
string[] ticksDirectories = Directory.GetDirectories(deterministicHashCodeRootDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string ticksDirectory in ticksDirectories)
{ {
ticksDirectoryName = Path.GetFileName(ticksDirectory); if (collection.Length != 1)
if (ticksDirectoryName.Length < 3 || ticksDirectoryName[0] != '(' || ticksDirectoryName[^1] != ')')
continue; continue;
personKeyDirectories = Directory.GetDirectories(ticksDirectory, "*", SearchOption.TopDirectoryOnly); foreach ((string personKey, Face? _, (string directory, string copyDirectory, string copyFileName, string shortcutFileName)) in collection)
foreach (string personKeyDirectory in personKeyDirectories)
{ {
personKey = Path.GetFileName(personKeyDirectory); if (string.IsNullOrEmpty(personKey))
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)
{
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)
{
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; continue;
reversedDeterministicHashCodeKey = Shared.Models.Stateless.Methods.INamed.GetReversedDeterministicHashCodeKey(keyValuePairsAny, keyValuePairs, file); if (item.Property?.Id is null || item.ImageFileHolder is null || item.ResizedFileHolder is null)
if (reversedDeterministicHashCodeKey is null)
continue; continue;
named.Add(new(personKey, reversedDeterministicHashCodeKey.Value)); minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
} if (minimumDateTime is null)
foreach (string personNameLinkDirectory in personNameLinkDirectories)
{
files = Directory.GetFiles(personNameLinkDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
if (!file.EndsWith(".lnk"))
continue; continue;
File.Delete(file); if (!Directory.Exists(directory))
{
_ = Directory.CreateDirectory(directory);
if (!string.IsNullOrEmpty(personKey) && _PeopleKeyValuePairs.ContainsKey(personKey))
{
person = _PeopleKeyValuePairs[personKey];
fullName = string.Concat(person.DisplayDirectoryName, ".txt");
File.WriteAllText(Path.Combine(directory, fullName), string.Empty);
} }
_ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(personNameLinkDirectory);
} }
_ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(personNameDirectory); 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);
} }
_ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(yearDirectory); 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(personKeyDirectory);
}
_ = 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(); if (!string.IsNullOrEmpty(_DeterministicHashCodeContentDirectory))
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 (!namedKeyValuePairs.ContainsKey(idAndNormalizedPixelPercentage)) Dictionary<int, List<Face>> keyValuePairs = new();
namedKeyValuePairs.Add(idAndNormalizedPixelPercentage, new()); List<(string PersonKey, double IdAndNormalizedPixelPercentage)> deterministicHashCodeCollection = new();
namedKeyValuePairs[idAndNormalizedPixelPercentage].Add(personKey); List<(string PersonKey, double IdAndNormalizedPixelPercentage)> incorrectDeterministicHashCodeCollection = new();
} foreach (Container container in containers)
foreach ((string personKey, double idAndNormalizedPixelPercentage) in incorrect)
{ {
if (!incorrectKeyValuePairs.ContainsKey(idAndNormalizedPixelPercentage)) foreach (Item item in container.Items)
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)
{
foreach (Shared.Models.Item item in container.Items)
{ {
if (item.ImageFileHolder is null || item.Property?.Id is null || !item.Faces.Any()) if (item.ImageFileHolder is null || item.Property?.Id is null || !item.Faces.Any())
continue; continue;
@ -229,7 +198,7 @@ public class MapLogic
keyValuePairs.Add(item.Property.Id.Value, item.Faces); 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; return result;
} }
public void AddToMapLogicAllCollection(Shared.Models.Item[] filteredItems) public void AddToMapLogicAllCollection(Item[] filteredItems)
{ {
if (_SixCharacterNamedFaceInfo.Any()) if (_SixCharacterNamedFaceInfo.Any())
{ {
string[] keys; string[] keys;
Shared.Models.Item item; Item item;
for (int i = 0; i < filteredItems.Length; i++) for (int i = 0; i < filteredItems.Length; i++)
{ {
item = filteredItems[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; Mapping mapping;
DateTime minimumDateTime; string personKey;
int? approximateYears;
string displayDirectoryName;
PersonBirthday? personBirthday;
double deterministicHashCodeKey; double deterministicHashCodeKey;
List<string> personKeys = new(); List<string> personKeys = new();
Shared.Models.PersonBirthday? personBirthday; (string DisplayDirectoryName, int? ApproximateYears, string Key, PersonBirthday[] PersonBirthdays) person;
foreach (Shared.Models.Item item in items) foreach (Item item in items)
{ {
if (item.ImageFileHolder is null) if (item.ImageFileHolder is null)
continue; continue;
if (item.Property?.Id is null || item.ResizedFileHolder is null) if (item.Property?.Id is null || item.ResizedFileHolder is null)
continue; continue;
foreach (Shared.Models.Face face in item.Faces) foreach (Face face in item.Faces)
{ {
personKeys.Clear(); personKeys.Clear();
if (face.LocationIndex is null) if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null)
continue; continue;
deterministicHashCodeKey = Shared.Models.Stateless.Methods.INamed.GetDeterministicHashCodeKey(item, face); deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face);
if (!_DeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey)) if (!_DeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey))
continue; continue;
minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
personKeys.AddRange(_DeterministicHashCodeKeyValuePairs[deterministicHashCodeKey]); personKeys.AddRange(_DeterministicHashCodeKeyValuePairs[deterministicHashCodeKey]);
(isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime);
for (int i = 0; i < personKeys.Count; i++) 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) if (personBirthday is null)
continue; continue;
if (face.Location is null) mapping = new(approximateYears, displayDirectoryName, face.Location.NormalizedPixelPercentage, personBirthday, personKey);
continue; item.Mapping.Add(mapping);
item.Named.Add(new(isWrongYear, minimumDateTime, face.Location.NormalizedPixelPercentage, personBirthday));
} }
} }
if (!personKeys.Any()) if (Shared.Models.Stateless.IMapping.UseDeterministicHashCodeUnknownFaceKeyValuePairsForAddToMapping && !personKeys.Any())
{ {
if (!_DeterministicHashCodeUnknownFaceKeyValuePairs.ContainsKey(item.Property.Id.Value)) if (!_DeterministicHashCodeUnknownFaceKeyValuePairs.ContainsKey(item.Property.Id.Value))
continue; continue;
minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
personKeys.AddRange(_DeterministicHashCodeUnknownFaceKeyValuePairs[item.Property.Id.Value]); personKeys.AddRange(_DeterministicHashCodeUnknownFaceKeyValuePairs[item.Property.Id.Value]);
(isWrongYear, _) = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime);
for (int i = 0; i < personKeys.Count; i++) 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) if (personBirthday is null)
continue; 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[] keys;
string directory; string directory;
string personKey; string personKey;
@ -366,15 +365,14 @@ public class MapLogic
string copyDirectory; string copyDirectory;
string? relativePath; string? relativePath;
string isWrongYearFlag; string isWrongYearFlag;
Shared.Models.Face face;
string shortcutFileName; string shortcutFileName;
Shared.Models.Item item;
string subDirectoryName; string subDirectoryName;
List<int> indices = new(); List<int> indices = new();
DateTime? minimumDateTime; DateTime? minimumDateTime;
List<Shared.Models.Face> faceCollection; DateTime dateTime = DateTime.Now;
Shared.Models.PersonBirthday? personBirthday; List<Face> faceCollection;
List<(string, Shared.Models.Face?, (string, string, string, string))> collection; PersonBirthday? personBirthday;
List<(string, Face?, (string, string, string, string))> collection;
for (int i = 0; i < items.Count; i++) for (int i = 0; i < items.Count; i++)
{ {
indices.Clear(); indices.Clear();
@ -433,7 +431,10 @@ public class MapLogic
if (timeSpan.Value.Ticks < 0) if (timeSpan.Value.Ticks < 0)
subDirectoryName = "!---"; subDirectoryName = "!---";
else else
subDirectoryName = $"^{Math.Floor(timeSpan.Value.TotalDays / 365):000}"; {
(years, _) = Shared.Models.Stateless.Methods.IPersonBirthday.GetAge(dateTime, personBirthday);
subDirectoryName = $"^{years:000}";
}
} }
face = faceCollection[zero]; face = faceCollection[zero];
directory = Path.Combine(dFacesContentDirectory, "Shortcuts", personKey, subDirectoryName); directory = Path.Combine(dFacesContentDirectory, "Shortcuts", personKey, subDirectoryName);
@ -457,86 +458,368 @@ public class MapLogic
return results; 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; 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()) if (!container.Items.Any())
continue; continue;
if (!container.SourceDirectory.StartsWith(argZero)) if (!container.SourceDirectory.StartsWith(argZero))
continue; 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; 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))
continue;
foreach (Shared.Models.Face face in item.Faces)
{ {
if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null) if (face.FaceEncoding is null || face.Location?.NormalizedPixelPercentage is null)
continue; continue;
if (named.PersonBirthday is null) deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(item, face);
if (_DeterministicHashCodeKeyValuePairs.ContainsKey(deterministicHashCodeKey))
continue; continue;
if (named.NormalizedPixelPercentage.HasValue && named.NormalizedPixelPercentage.Value != face.Location?.NormalizedPixelPercentage) 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; continue;
key = GetKey(named.MinimumDateTime, named.IsWrongYear, named.PersonBirthday); dateKey = Stateless.MapLogic.GetDateKey(dateTime, closest.Mapping, closest.MinimumDateTime, closest.IsWrongYear);
if (!results.ContainsKey(key)) key = string.Concat(closest.Mapping.PersonKey, dateKey);
results.Add(key, new()); if (!keyValuePairs.ContainsKey(key))
results[key].Add(new(named.MinimumDateTime, named.IsWrongYear, named.PersonBirthday, face)); keyValuePairs.Add(key, 0);
if (named.NormalizedPixelPercentage is null) else if (keyValuePairs[key] > Shared.Models.Stateless.IClosest.MaximumPer)
continue;
keyValuePairs[key] += 1;
item.Closest.Add(closest);
break; 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 (used.Contains(face.Location.NormalizedPixelPercentage.Value))
continue;
// 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);
}
}
}
}
return results; 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; WindowsShortcut windowsShortcut;
if (item.Property is null || item.ImageFileHolder is null) string[] directories = (from l in collection select l.directory).Distinct().ToArray();
throw new NullReferenceException(); foreach (string directory in directories)
DateTime? minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); {
result = item.Property.IsWrongYear(item.ImageFileHolder.FullName, minimumDateTime); if (string.IsNullOrEmpty(directory))
return result; 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);
} }
} }

View 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);
}

View 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;
}
}

View 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;
}
}

View File

@ -295,7 +295,7 @@ public class PropertyCompareLogic
continue; continue;
totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); 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); 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 try
{ {

View File

@ -571,7 +571,7 @@ public class A_Property
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; 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}"; 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); using ProgressBar progressBar = new(filteredItems.Length, message, options);
_ = Parallel.For(0, filteredItems.Length, parallelOptions, i => _ = Parallel.For(0, filteredItems.Length, parallelOptions, (i, state) =>
{ {
try try
{ {

View File

@ -140,7 +140,7 @@ public class Container
List<(int, string, List<(string, Shared.Models.Property?)>, int)> results = new(); List<(int, string, List<(string, Shared.Models.Property?)>, int)> results = new();
int length = rootDirectory.Length; int length = rootDirectory.Length;
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = Environment.ProcessorCount }; 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; return results;
} }

View File

@ -6,38 +6,33 @@ namespace View_by_Distance.Shared.Models;
public class Closest : Properties.IClosest public class Closest : Properties.IClosest
{ {
protected readonly double? _Average; protected readonly int _Average;
protected readonly int? _NormalizedPixelPercentage;
protected readonly bool? _IsWrongYear; protected readonly bool? _IsWrongYear;
protected readonly double? _Minimum; protected Mapping _Mapping;
protected readonly double _Minimum;
protected readonly DateTime _MinimumDateTime; protected readonly DateTime _MinimumDateTime;
protected readonly PersonBirthday? _PersonBirthday; protected readonly int _NormalizedPixelPercentage;
public double? Average => _Average; protected readonly long? _TicksDelta;
public int? NormalizedPixelPercentage => _NormalizedPixelPercentage; public double Average => _Average;
public bool? IsWrongYear => _IsWrongYear; public bool? IsWrongYear => _IsWrongYear;
public double? Minimum => _Minimum; public Mapping Mapping => _Mapping;
public double Minimum => _Minimum;
public DateTime MinimumDateTime => _MinimumDateTime; public DateTime MinimumDateTime => _MinimumDateTime;
public PersonBirthday? PersonBirthday => _PersonBirthday; public int NormalizedPixelPercentage => _NormalizedPixelPercentage;
public long? TicksDelta => _TicksDelta;
[JsonConstructor] [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; _Average = average;
_NormalizedPixelPercentage = normalizedPixelPercentage; _NormalizedPixelPercentage = normalizedPixelPercentage;
_IsWrongYear = isWrongYear; _IsWrongYear = isWrongYear;
_Mapping = mapping;
_Minimum = minimum; _Minimum = minimum;
_MinimumDateTime = minimumDateTime; _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() public override string ToString()
{ {
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });

View File

@ -7,6 +7,7 @@ public class Face : Properties.IFace
{ {
protected DateTime _DateTime; protected DateTime _DateTime;
protected List<FaceDistance> _FaceDistances;
protected FaceEncoding? _FaceEncoding; protected FaceEncoding? _FaceEncoding;
protected Dictionary<Stateless.FacePart, FacePoint[]>? _FaceParts; protected Dictionary<Stateless.FacePart, FacePoint[]>? _FaceParts;
protected readonly OutputResolution? _OutputResolution; protected readonly OutputResolution? _OutputResolution;
@ -14,6 +15,7 @@ public class Face : Properties.IFace
protected readonly int? _LocationIndex; protected readonly int? _LocationIndex;
protected readonly string _RelativePath; protected readonly string _RelativePath;
public DateTime DateTime => _DateTime; public DateTime DateTime => _DateTime;
public List<FaceDistance> FaceDistances => _FaceDistances;
public FaceEncoding? FaceEncoding => _FaceEncoding; public FaceEncoding? FaceEncoding => _FaceEncoding;
public Dictionary<Stateless.FacePart, FacePoint[]>? FaceParts => _FaceParts; public Dictionary<Stateless.FacePart, FacePoint[]>? FaceParts => _FaceParts;
public Location? Location => _Location; public Location? Location => _Location;
@ -22,9 +24,12 @@ public class Face : Properties.IFace
public string RelativePath => _RelativePath; public string RelativePath => _RelativePath;
[JsonConstructor] [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; _DateTime = dateTime;
_FaceDistances = faceDistances;
_FaceEncoding = faceEncoding; _FaceEncoding = faceEncoding;
_FaceParts = faceParts; _FaceParts = faceParts;
_Location = location; _Location = location;
@ -34,29 +39,29 @@ public class Face : Properties.IFace
} }
public Face() : 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) : 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) : 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) 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); _Location = new(face.Location.Confidence, face.OutputResolution.Height, face.Location, face.OutputResolution.Width, facesCount);
} }
public Face(int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, Face face) : 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) if (outputResolutionHeight > 0)
_OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth); _OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth);
} }
public Face(Property property, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, string relativePath, int? i, Location? location) : 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; DateTime?[] dateTimes;
_OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth); _OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth);

View 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;
}
}

View File

@ -11,8 +11,8 @@ public class Item : Properties.IItem
protected List<Closest> _Closest; protected List<Closest> _Closest;
protected List<Face> _Faces; protected List<Face> _Faces;
protected readonly FileHolder? _ImageFileHolder; protected readonly FileHolder? _ImageFileHolder;
protected List<Mapping> _Mapping;
protected bool? _Moved; protected bool? _Moved;
protected List<Named> _Named;
protected readonly bool? _NoJson; protected readonly bool? _NoJson;
protected Property? _Property; protected Property? _Property;
protected readonly string _RelativePath; protected readonly string _RelativePath;
@ -24,9 +24,9 @@ public class Item : Properties.IItem
public List<Closest> Closest => _Closest; public List<Closest> Closest => _Closest;
public List<Face> Faces => _Faces; public List<Face> Faces => _Faces;
public FileHolder? ImageFileHolder => _ImageFileHolder; public FileHolder? ImageFileHolder => _ImageFileHolder;
public List<Mapping> Mapping => _Mapping;
public bool? Moved => _Moved; public bool? Moved => _Moved;
public bool? NoJson => _NoJson; public bool? NoJson => _NoJson;
public List<Named> Named => _Named;
public Property? Property => _Property; public Property? Property => _Property;
public string RelativePath => _RelativePath; public string RelativePath => _RelativePath;
public FileHolder? ResizedFileHolder => _ResizedFileHolder; public FileHolder? ResizedFileHolder => _ResizedFileHolder;
@ -34,15 +34,15 @@ public class Item : Properties.IItem
public bool ValidImageFormatExtension => _ValidImageFormatExtension; public bool ValidImageFormatExtension => _ValidImageFormatExtension;
[JsonConstructor] [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; _Abandoned = abandoned;
_Changed = changed; _Changed = changed;
_Closest = closest; _Closest = closest;
_Faces = faces; _Faces = faces;
_ImageFileHolder = imageFileHolder; _ImageFileHolder = imageFileHolder;
_Mapping = mapping;
_Moved = moved; _Moved = moved;
_Named = named;
_NoJson = noJson; _NoJson = noJson;
_Property = property; _Property = property;
_RelativePath = relativePath; _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) public Item(string sourceDirectoryFile, string relativePath, FileHolder? imageFileInfo, bool isValidImageFormatExtension, Property? property, bool? abandoned, bool? changed)
{ {
_Faces = new(); _Faces = new();
_Named = new(); _Mapping = new();
_Closest = new(); _Closest = new();
_Changed = changed; _Changed = changed;
_Property = property; _Property = property;

View File

@ -116,7 +116,7 @@ public class Location : Properties.ILocation, IEquatable<Location>
value = at / total; value = at / total;
if (value < 0) if (value < 0)
value = 3; 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; return result;
} }

49
Shared/Models/Mapping.cs Normal file
View 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;
}

View 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;
}

View File

@ -1,6 +1,6 @@
namespace View_by_Distance.Shared.Models.Methods; namespace View_by_Distance.Shared.Models.Methods;
public interface INamed : Stateless.Methods.INamed public interface INamed : Stateless.Methods.IMapping
{ // ... { // ...
// //

View File

@ -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;
}
}

View File

@ -1,3 +1,4 @@
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
@ -53,4 +54,18 @@ public class Person : Properties.IPerson
return result; 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();
}
} }

View File

@ -3,11 +3,12 @@ namespace View_by_Distance.Shared.Models.Properties;
public interface IClosest public interface IClosest
{ {
public double? Average { get; } public double Average { get; }
public int? NormalizedPixelPercentage { get; }
public bool? IsWrongYear { get; } public bool? IsWrongYear { get; }
public double? Minimum { get; } public Mapping Mapping { get; }
public double Minimum { get; }
public DateTime MinimumDateTime { get; } public DateTime MinimumDateTime { get; }
public PersonBirthday? PersonBirthday { get; } public int NormalizedPixelPercentage { get; }
public long? TicksDelta { get; }
} }

View File

@ -4,6 +4,7 @@ public interface IFace
{ {
public DateTime DateTime { get; } public DateTime DateTime { get; }
public List<FaceDistance> FaceDistances { get; }
public FaceEncoding? FaceEncoding { get; } public FaceEncoding? FaceEncoding { get; }
public Dictionary<Stateless.FacePart, FacePoint[]>? FaceParts { get; } public Dictionary<Stateless.FacePart, FacePoint[]>? FaceParts { get; }
public Location? Location { get; } public Location? Location { get; }

View File

@ -1,11 +1,12 @@
namespace View_by_Distance.Shared.Models.Properties; namespace View_by_Distance.Shared.Models.Properties;
public interface INamed public interface IFaceDistance
{ {
public List<double> Distances { get; }
public bool? IsWrongYear { get; } public bool? IsWrongYear { get; }
public string Key { get; }
public Mapping Mapping { get; }
public DateTime MinimumDateTime { get; } public DateTime MinimumDateTime { get; }
public int? NormalizedPixelPercentage { get; }
public PersonBirthday? PersonBirthday { get; }
} }

View File

@ -8,9 +8,9 @@ public interface IItem
public List<Closest> Closest { get; } public List<Closest> Closest { get; }
public List<Face> Faces { get; } public List<Face> Faces { get; }
public FileHolder? ImageFileHolder { get; } public FileHolder? ImageFileHolder { get; }
public List<Mapping> Mapping { get; }
public bool? Moved { get; } public bool? Moved { get; }
public bool? NoJson { get; } public bool? NoJson { get; }
public List<Named> Named { get; }
public Property? Property { get; } public Property? Property { get; }
public string RelativePath { get; } public string RelativePath { get; }
public FileHolder? ResizedFileHolder { get; } public FileHolder? ResizedFileHolder { get; }

View 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; }
}

View 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; }
}

View File

@ -3,9 +3,14 @@
public interface IClosest 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 int MaximumPer = 50;
const float MaximumMinimum = 0.50f;
const bool SkipIsWrongYear = true;
const float MinimumMinimum = 0.05f;
} }

View 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;
}

View File

@ -3,7 +3,7 @@
public interface ILocation public interface ILocation
{ {
const int Decimals = 6; const int Digits = 6;
const int Factor = 1000000; const int Factor = 1000000;
} }

View 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;
}

View File

@ -0,0 +1,8 @@
namespace View_by_Distance.Shared.Models.Stateless;
public interface IPersonBirthday
{
const string Format = "yyyy-MM-dd_HH";
}

View 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);
}
}

View File

@ -3,6 +3,33 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods;
internal abstract class Closest 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;
}
} }

View 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);
}

View File

@ -3,8 +3,8 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IClosest 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);
} }

View File

@ -1,18 +1,18 @@
namespace View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface INamed public interface IMapping
{ // ... { // ...
double? TestStatic_GetReversedDeterministicHashCodeKey(string file); double? TestStatic_GetReversedDeterministicHashCodeKey(string file);
static double? GetReversedDeterministicHashCodeKey(bool keyValuePairsAny, Dictionary<int, List<Models.Face>> keyValuePairs, 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); double TestStatic_GetDeterministicHashCodeKey(Models.Item item, Models.Face face);
static double 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); double TestStatic_GetDeterministicHashCodeKey(Models.Item item, Models.Closest closest);
static double GetDeterministicHashCodeKey(Models.Item item, Models.Closest closest) => static double GetDeterministicHashCodeKey(Models.Item item, Models.Closest closest) =>
Named.GetDeterministicHashCodeKey(item, closest); Mapping.GetDeterministicHashCodeKey(item, closest);
} }

View File

@ -3,37 +3,65 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IPersonBirthday 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(); double? TestStatic_GetAge(Models.PersonBirthday birthday);
static string GetFormat() => PersonBirthday.GetFormat(); static double? GetAge(Models.PersonBirthday birthday) =>
PersonBirthday.GetAge(birthday);
DateTime? TestStatic_GetDateTime(string personKey) => PersonBirthday.GetDateTime(personKey); DateTime? TestStatic_GetDateTime(string personKey) =>
static DateTime? GetDateTime(string personKey) => PersonBirthday.GetDateTime(personKey); PersonBirthday.GetDateTime(personKey);
static DateTime? GetDateTime(string personKey) =>
PersonBirthday.GetDateTime(personKey);
string TestStatic_GetFileName(Models.PersonBirthday personBirthday) => PersonBirthday.GetFileName(personBirthday); string TestStatic_GetFileName(Models.PersonBirthday personBirthday) =>
static string GetFileName(Models.PersonBirthday personBirthday) => PersonBirthday.GetFileName(personBirthday); PersonBirthday.GetFileName(personBirthday);
static string GetFileName(Models.PersonBirthday personBirthday) =>
PersonBirthday.GetFileName(personBirthday);
Models.PersonBirthday? TestStatic_GetPersonBirthday(string personKey) => PersonBirthday.GetPersonBirthday(personKey); (int, TimeSpan) TestStatic_GetAge(DateTime dateTime, Models.PersonBirthday birthday);
static Models.PersonBirthday? GetPersonBirthday(string personKey) => PersonBirthday.GetPersonBirthday(personKey); static (int, TimeSpan) GetAge(DateTime dateTime, Models.PersonBirthday birthday) =>
PersonBirthday.GetAge(dateTime, birthday);
string TestStatic_GetFormatted(Models.PersonBirthday personBirthday) => PersonBirthday.GetFormatted(personBirthday); string TestStatic_GetFormatted(Models.PersonBirthday personBirthday) =>
static string GetFormatted(Models.PersonBirthday personBirthday) => PersonBirthday.GetFormatted(personBirthday); PersonBirthday.GetFormatted(personBirthday);
static string GetFormatted(Models.PersonBirthday personBirthday) =>
PersonBirthday.GetFormatted(personBirthday);
Models.PersonBirthday TestStatic_GetNextBirthDate(Properties.IStorage storage) => PersonBirthday.GetNextBirthDate(storage); Models.PersonBirthday? TestStatic_GetPersonBirthday(string personKey) =>
static Models.PersonBirthday GetNextBirthDate(Properties.IStorage storage) => PersonBirthday.GetNextBirthDate(storage); PersonBirthday.GetPersonBirthday(personKey);
static Models.PersonBirthday? GetPersonBirthday(string personKey) =>
PersonBirthday.GetPersonBirthday(personKey);
bool TestStatic_DoesBirthDateExits(Properties.IStorage storage, Models.PersonBirthday personBirthday) => DoesBirthDateExits(storage, personBirthday); Models.PersonBirthday TestStatic_GetNextBirthDate(Properties.IStorage storage) =>
static bool DoesBirthDateExits(Properties.IStorage storage, Models.PersonBirthday personBirthday) => DoesBirthDateExits(storage, personBirthday); 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); TimeSpan? TestStatic_Get(DateTime now, Models.PersonBirthday personBirthday) =>
static string GetFileFullName(Properties.IStorage storage, Models.PersonBirthday personBirthday) => PersonBirthday.GetFileFullName(storage, 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); string TestStatic_GetFileFullName(Properties.IStorage storage, Models.PersonBirthday personBirthday) =>
static TimeSpan? GetTimeSpan(DateTime minimumDateTime, bool? isWrongYear, Models.PersonBirthday personBirthday) => PersonBirthday.GetTimeSpan(minimumDateTime, isWrongYear, 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);
} }

View File

@ -2,18 +2,18 @@ using System.Text.Json;
namespace View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Shared.Models.Stateless.Methods;
internal abstract class Named internal abstract class Mapping
{ {
private static double GetDeterministicHashCodeKey(int id, int normalizedPixelPercentage) 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) internal static double GetDeterministicHashCodeKey(Models.Item item, Models.Closest closest)
{ {
double result; 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(); throw new NullReferenceException();
result = GetDeterministicHashCodeKey(item.Property.Id.Value, closest.NormalizedPixelPercentage.Value); result = GetDeterministicHashCodeKey(item.Property.Id.Value, closest.NormalizedPixelPercentage);
return result; return result;
} }

View File

@ -1,4 +1,5 @@
using System.Text.Json; using System.Text.Json;
using System.Text.RegularExpressions;
namespace View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Shared.Models.Stateless.Methods;
@ -166,9 +167,9 @@ internal abstract class Person
Models.PersonName name; Models.PersonName name;
List<Models.PersonURL> urls; List<Models.PersonURL> urls;
Models.PersonBirthday birthday; Models.PersonBirthday birthday;
List<Models.PersonComment> comments;
List<Models.PersonEmail> emails = new(); List<Models.PersonEmail> emails = new();
List<Models.PersonNumber> numbers = new(); List<Models.PersonNumber> numbers = new();
List<Models.PersonComment> comments = new();
List<Models.PersonAddress> addresses = new(); List<Models.PersonAddress> addresses = new();
Dictionary<DateTime, PersonImport> keyValuePairs = GetPersonCollection(localKnownPeopleFile); Dictionary<DateTime, PersonImport> keyValuePairs = GetPersonCollection(localKnownPeopleFile);
foreach (KeyValuePair<DateTime, PersonImport> keyValuePair in keyValuePairs) foreach (KeyValuePair<DateTime, PersonImport> keyValuePair in keyValuePairs)
@ -176,6 +177,7 @@ internal abstract class Person
if (string.IsNullOrEmpty(keyValuePair.Value.Name)) if (string.IsNullOrEmpty(keyValuePair.Value.Name))
continue; continue;
urls = new(); urls = new();
comments = new();
birthday = new(keyValuePair.Key); birthday = new(keyValuePair.Key);
name = PersonName.Create(keyValuePair.Value.Name); name = PersonName.Create(keyValuePair.Value.Name);
if (name.First is null || string.IsNullOrEmpty(name.First.Value)) if (name.First is null || string.IsNullOrEmpty(name.First.Value))
@ -242,7 +244,54 @@ internal abstract class Person
results = GetPeopleFromText(storage, localKnownPeopleFile); results = GetPeopleFromText(storage, localKnownPeopleFile);
} }
} }
SaveToDirectory(storage, results);
return results.ToArray(); 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"));
}
}
}
} }

View File

@ -8,14 +8,12 @@ internal abstract class PersonBirthday
internal static DateTime GetDefaultValue() => DateTime.MinValue; // {{1}}SingletonValue internal static DateTime GetDefaultValue() => DateTime.MinValue; // {{1}}SingletonValue
// ... // ...
internal static string GetFormatted(Models.PersonBirthday personBirthday) => personBirthday.Value.ToString(Stateless.IPersonBirthday.Format);
internal static string GetFormat() => "yyyy-MM-dd_HH"; internal static string GetFileName(Models.PersonBirthday personBirthday) => $"{personBirthday.Value.ToString(Stateless.IPersonBirthday.Format)}.json";
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 bool DoesBirthDateExits(Properties.IStorage storage, Models.PersonBirthday personBirthday) => File.Exists(GetFileFullName(storage, personBirthday)); 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 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 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) internal static Models.PersonBirthday? GetPersonBirthday(string personKey)
{ {
@ -37,4 +35,41 @@ internal abstract class PersonBirthday
timeSpan = new(minimumDateTime.Ticks - personBirthday.Value.Ticks); timeSpan = new(minimumDateTime.Ticks - personBirthday.Value.Ticks);
return timeSpan; 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;
}
} }

View File

@ -62,10 +62,10 @@ internal abstract class Property
in segments in segments
where l?.Length > 2 where l?.Length > 2
&& ( && (
l[..2] is "19" or "20" l[..2] is "18" or "19" or "20"
|| (l.Length == 5 && l.Substring(1, 2) is "19" or "20" && (l[0] is '~' or '=' or '-' or '^' or '#')) || (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 "19" or "20" && l[4] == '.') || (l.Length == 6 && l[..2] is "18" or "19" or "20" && l[4] == '.')
|| (l.Length == 7 && l.Substring(1, 2) is "19" or "20" && l[5] == '.') || (l.Length == 7 && l.Substring(1, 2) is "18" or "19" or "20" && l[5] == '.')
) )
select l select l
).ToArray(); ).ToArray();

View 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);
}
}