2023-10-20 19:37:19 -07:00

522 lines
25 KiB
C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Phares.Shared;
using System.Globalization;
using System.Text;
using View_by_Distance.Date.Group.Models;
using View_by_Distance.Property.Models;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Methods;
using WindowsShortcutFactory;
namespace View_by_Distance.Date.Group;
public class DateGroup
{
private readonly AppSettings _AppSettings;
private readonly List<string> _Exceptions;
private readonly ILogger<Program>? _Logger;
private readonly IsEnvironment _IsEnvironment;
private readonly Models.Configuration _Configuration;
private readonly List<KeyValuePair<string, string>> _FileKeyValuePairs;
private readonly Dictionary<string, List<Tuple<string, Shared.Models.Property>>> _FilePropertiesKeyValuePairs;
public DateGroup(List<string> args, ILogger<Program>? logger, IsEnvironment isEnvironment, IConfigurationRoot configurationRoot, AppSettings appSettings, string workingDirectory, bool isSilent, IConsole console)
{
if (isSilent)
{ }
if (args is null)
{ }
if (console is null)
{ }
_Logger = logger;
_AppSettings = appSettings;
_IsEnvironment = isEnvironment;
_Exceptions = [];
_FileKeyValuePairs = [];
_FilePropertiesKeyValuePairs = [];
Property.Models.Configuration propertyConfiguration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot);
Models.Configuration configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration);
_Logger?.LogInformation(propertyConfiguration.RootDirectory);
Property.Models.Configuration.Verify(propertyConfiguration, requireExist: true);
Verify(configuration);
bool reverse = false;
_Configuration = configuration;
if (!_IsEnvironment.Development)
throw new Exception("This program only allows development environments!");
long ticks = DateTime.Now.Ticks;
Metadata.Models.B_Metadata metadata = new(propertyConfiguration);
string[] dbFiles = Directory.GetFiles(propertyConfiguration.RootDirectory, "*.db", SearchOption.AllDirectories);
foreach (string dbFile in dbFiles)
File.Delete(dbFile);
for (int i = 1; i < 10; i++)
_ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(propertyConfiguration.RootDirectory);
string destinationRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(propertyConfiguration, "Z) Moved");
string aResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property));
string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}");
if (!Directory.Exists(aPropertySingletonDirectory))
_ = Directory.CreateDirectory(aPropertySingletonDirectory);
(int t, Container[] containers) = Shared.Models.Stateless.Methods.IContainer.GetContainers(propertyConfiguration, aPropertySingletonDirectory);
A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory, aResultsFullGroupDirectory);
if (propertyLogic.ExceptionsDirectories.Count != 0)
throw new Exception();
if (propertyConfiguration.PopulatePropertyId && (configuration.ByCreateDateShortcut || configuration.ByHash) && Shared.Models.Stateless.Methods.IProperty.Any(containers))
{
propertyLogic.SavePropertyParallelWork(ticks, metadata, t, containers);
if (propertyLogic.ExceptionsDirectories.Count != 0)
throw new Exception();
}
if (configuration.ByCreateDateShortcut)
CreateDateShortcut(propertyConfiguration, containers);
else
MoveFiles(propertyConfiguration, destinationRoot, containers);
}
private static void Verify(Models.Configuration configuration)
{
int check = 0;
if (configuration.ByCreateDateShortcut)
check += 1;
if (configuration.ByDay)
check += 1;
if (configuration.ByHash)
check += 1;
if (configuration.BySeason)
check += 1;
if (configuration.ByWeek)
check += 1;
if (check != 1)
throw new Exception("Change configuration!");
if (configuration?.PropertyConfiguration?.PopulatePropertyId is null)
throw new NullReferenceException(nameof(configuration.PropertyConfiguration.PopulatePropertyId));
if (configuration.PropertyConfiguration.PopulatePropertyId && !configuration.ByCreateDateShortcut && !configuration.ByHash)
throw new Exception("Change configuration!");
if (!configuration.PropertyConfiguration.PopulatePropertyId && configuration.ByHash)
throw new Exception("Change configuration!");
}
private static bool WriteAllText(string path, string contents, bool compareBeforeWrite)
{
bool result;
string text;
if (!compareBeforeWrite)
result = true;
else
{
if (!File.Exists(path))
text = string.Empty;
else
text = File.ReadAllText(path);
result = text != contents;
}
if (result)
{
if (path.Contains("()"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("[]") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && path.EndsWith(".json") && contents[0] == '{')
File.WriteAllText(path, contents);
else if (path.Contains("[]") && path.EndsWith(".json") && contents[0] == '[')
File.WriteAllText(path, contents);
else
File.WriteAllText(path, contents);
}
return result;
}
private List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> GetMoveFileCollection(string destinationDirectory, string topDirectory, Item[] filteredItems)
{
List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> results = [];
char flag;
string day;
int season;
string year;
string month;
string? check;
string fileName;
string? pathRoot;
DateTime dateTime;
string seasonName;
string weekOfYear;
bool? isWrongYear;
string seasonValue;
string directoryName;
string topDirectoryName;
List<DateTime> dateTimes;
string[]? matches = null;
string[] directorySegments;
List<string> destinationCollection;
List<string> directoryNames = [];
List<string> topDirectorySegments = [];
StringBuilder destinationDirectoryName = new();
Calendar calendar = new CultureInfo("en-US").Calendar;
for (int z = 1; z < 3; z++)
{
if (z == 1)
{
check = Path.Combine(destinationDirectory, ".");
pathRoot = Path.GetPathRoot(destinationDirectory);
}
else if (z == 2)
{
check = Path.Combine(topDirectory, ".");
pathRoot = Path.GetPathRoot(topDirectory);
}
else
throw new Exception();
if (string.IsNullOrEmpty(pathRoot))
continue;
for (int i = 0; i < int.MaxValue; i++)
{
check = Path.GetDirectoryName(check);
if (string.IsNullOrEmpty(check) || check == pathRoot)
break;
directoryName = Path.GetFileName(check);
directorySegments = directoryName.Split(' ');
topDirectorySegments.AddRange(directorySegments);
(_, matches) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(directorySegments, string.Empty);
if (matches.Length != 0)
break;
}
if (matches is not null && matches.Length != 0)
break;
}
foreach (Item item in filteredItems)
{
directoryNames.Clear();
destinationCollection = [];
_ = destinationDirectoryName.Clear();
if (item.Property is not null)
dateTimes = item.Property.GetDateTimes();
else
{
if (item.ImageFileHolder.LastWriteTime is null)
throw new Exception();
dateTimes = [item.ImageFileHolder.LastWriteTime.Value];
}
if (item.Property is not null && item.Property.DateTimeOriginal is not null)
dateTime = item.Property.DateTimeOriginal.Value;
else if (item.ImageFileHolder.LastWriteTime is not null && item.Property is null)
dateTime = item.ImageFileHolder.LastWriteTime.Value;
else
dateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
day = dateTime.ToString("MM-dd");
month = dateTime.ToString("MMMM");
if (item.Property?.Id is null)
{
flag = '#';
isWrongYear = null;
}
else
{
(isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.ImageFileHolder, item.Property.DateTimeOriginal, dateTimes);
if (isWrongYear is null)
flag = '#';
else if (isWrongYear.Value)
flag = '~';
else
{
if (item.Property.DateTimeOriginal is not null && dateTime.DayOfYear != item.Property.DateTimeOriginal.Value.DayOfYear && Math.Abs(new TimeSpan(dateTime.Ticks - item.Property.DateTimeOriginal.Value.Ticks).TotalHours) > 8)
flag = '^';
else
flag = '=';
}
}
(season, seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear);
if ((from l in topDirectorySegments where l == "Christmas" select true).Any())
seasonValue = string.Empty;
else
seasonValue = $".{season}";
if (isWrongYear is null || !isWrongYear.Value)
year = $"{flag}{dateTime:yyyy}{seasonValue}";
else if (matches is null || matches.Length < 3)
year = "----";
else
{
if (matches[0][0] != '~')
year = $"{flag}{matches[0].Split('.')[0]}{seasonValue}";
else
year = $"{flag}{matches[0][1..].Split('.')[0]}{seasonValue}";
}
topDirectoryName = Path.GetFileName(topDirectory);
weekOfYear = calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
if (_Configuration.ByHash)
directoryNames.Add($"{year} {seasonName}");
else if (_Configuration.BySeason && topDirectoryName.Length == 1 && topDirectoryName[0] == '_')
directoryNames.Add($"{year} {seasonName}");
else
{
if (!_Configuration.KeepFullPath)
{
if (topDirectoryName.Length > 1)
_ = destinationDirectoryName.Append(topDirectoryName);
if (_Configuration.BySeason)
directoryNames.AddRange(new string[] { $"{destinationDirectoryName} {year} {seasonName}" });
else if (_Configuration.ByDay)
directoryNames.AddRange(new string[] { $"{destinationDirectoryName} {year}", $"{weekOfYear}) {year}-{day}" });
else if (_Configuration.ByWeek)
directoryNames.AddRange(new string[] { $"{destinationDirectoryName} {year}", $"{weekOfYear}) {year} {month}" });
else
throw new Exception();
}
else
{
foreach (string sourceDirectoryNameSegment in topDirectorySegments)
{
if (matches is not null && matches.Contains(sourceDirectoryNameSegment))
_ = destinationDirectoryName.Append(year);
else
_ = destinationDirectoryName.Append(sourceDirectoryNameSegment);
}
if (_Configuration.BySeason)
directoryNames.Add($"{year} {seasonName}");
else if (_Configuration.ByDay)
directoryNames.Add($"{weekOfYear}) {year} {day}");
else if (_Configuration.ByWeek)
directoryNames.Add($"{weekOfYear}) {month} {year}");
else
throw new Exception();
}
}
if (!_Configuration.ByHash || item.Property?.Id is null)
fileName = item.ImageFileHolder.Name;
else
fileName = $"{item.Property.Id.Value}{item.ImageFileHolder.ExtensionLowered}";
destinationCollection.Add(destinationDirectory);
destinationCollection.AddRange(directoryNames);
destinationCollection.Add(fileName);
if (item.ImageFileHolder.LastWriteTime is null)
continue;
if (item.Property is not null)
results.Add(new(item, item.Property.LastWriteTime.Ticks, dateTime.Ticks, destinationCollection.ToArray()));
else
results.Add(new(item, item.ImageFileHolder.LastWriteTime.Value.Ticks, dateTime.Ticks, destinationCollection.ToArray()));
}
return results;
}
private A_Property GetPropertyLogic(bool reverse, string outputExtension, string aResultsFullGroupDirectory)
{
A_Property result;
if (_Configuration?.PropertyConfiguration is null)
throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration));
result = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, outputExtension, reverse, aResultsFullGroupDirectory);
return result;
}
private static Item[] GetFilterItems(Container container)
{
List<Item> results = [];
foreach (Item item in container.Items)
{
if (item.ImageFileHolder is not null)
results.Add(item);
}
return results.ToArray();
}
private (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] GetFileMoveCollectionAll(Property.Models.Configuration configuration, string destinationRoot, Container[] containers)
{
(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] results;
Item[] filteredItems;
string? topDirectory;
string? checkDirectory;
string destinationDirectory;
List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollection = [];
List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollectionDirectory;
foreach (Container container in containers)
{
if (container.Items.Count == 0)
continue;
if (!_Configuration.KeepFullPath)
destinationDirectory = destinationRoot;
else
destinationDirectory = string.Concat(destinationRoot, container.SourceDirectory[configuration.RootDirectory.Length..]);
checkDirectory = Path.GetFullPath(container.SourceDirectory);
for (int z = 0; z < int.MaxValue; z++)
{
if (checkDirectory == configuration.RootDirectory)
break;
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory))
break;
}
if (string.IsNullOrEmpty(checkDirectory))
continue;
topDirectory = checkDirectory;
filteredItems = GetFilterItems(container);
if (filteredItems.Length == 0)
continue;
fileMoveCollectionDirectory = GetMoveFileCollection(destinationDirectory, topDirectory, filteredItems);
fileMoveCollection.AddRange(fileMoveCollectionDirectory);
}
results = (from l in fileMoveCollection orderby l.MinimumDateTimeTicks descending, l.LastWriteTimeTicks descending select l).ToArray();
return results;
}
private void MoveFiles(Property.Models.Configuration configuration, string destinationRoot, Container[] containers)
{
string checkDirectory;
bool hasDuplicate;
string fullFileName;
string directoryName;
WindowsShortcut windowsShortcut;
string duplicate = "-Duplicates";
List<string> filesDistinct = [];
List<string> filesDuplicate = [];
List<string> directoriesDistinct = [];
(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] fileMoveCollectionAll = GetFileMoveCollectionAll(configuration, destinationRoot, containers);
foreach ((Item item, long lastWriteTimeTicks, long minimumDateTimeTicks, string[] destination) in fileMoveCollectionAll)
{
fullFileName = Path.Combine(destination);
if (filesDistinct.Contains(fullFileName))
filesDuplicate.Add(fullFileName);
filesDistinct.Add(fullFileName);
directoryName = Path.Combine(destination.Take(destination.Length - 1).ToArray());
if (directoriesDistinct.Contains(directoryName))
continue;
directoriesDistinct.Add(directoryName);
if (!Directory.Exists(directoryName))
_ = Directory.CreateDirectory(directoryName);
checkDirectory = Path.Combine(string.Concat(directoryName, duplicate), "I");
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
for (int i = 0; i < 200 - checkDirectory.Length; i++)
{
checkDirectory = string.Concat(checkDirectory, "I");
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
}
}
_Logger?.LogInformation($"Ready to move {fileMoveCollectionAll.Length} file(s)?");
for (int y = 0; y < int.MaxValue; y++)
{
_Logger?.LogInformation("Press \"Y\" key to move file(s) or close console to not move files");
if (System.Console.ReadKey().Key == ConsoleKey.Y)
break;
}
_Logger?.LogInformation(". . .");
int moved = 0;
foreach ((Item item, long lastWriteTimeTicks, long minimumDateTimeTicks, string[] destination) in fileMoveCollectionAll)
{
fullFileName = Path.Combine(destination);
hasDuplicate = filesDuplicate.Contains(fullFileName);
if (hasDuplicate)
{
destination[0] = Path.Combine(string.Concat(destination[0], duplicate), "I");
fullFileName = Path.Combine(destination);
}
for (int i = 0; i < 200 - destination[0].Length; i++)
{
if (!File.Exists(fullFileName))
break;
else
{
destination[0] = string.Concat(destination[0], "I");
fullFileName = Path.Combine(destination);
if (File.Exists(fullFileName))
continue;
}
}
File.Move(item.ImageFileHolder.FullName, fullFileName);
moved += 1;
if (hasDuplicate)
{
try
{
windowsShortcut = new() { Path = item.ImageFileHolder.DirectoryName, Description = item.ImageFileHolder.Name };
windowsShortcut.Save(string.Concat(fullFileName, ".lnk"));
windowsShortcut.Dispose();
}
catch (Exception) { }
}
}
_ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(destinationRoot);
_Logger?.LogInformation($"{moved} file(s) moved");
for (int y = 0; y < int.MaxValue; y++)
{
_Logger?.LogInformation("Press \"Y\" key to move file(s) back or close console to leave them moved");
if (System.Console.ReadKey().Key == ConsoleKey.Y)
break;
}
_Logger?.LogInformation(". . .");
foreach ((Item item, long lastWriteTimeTicks, long minimumDateTimeTicks, string[] destination) in fileMoveCollectionAll)
{
fullFileName = Path.Combine(destination);
if (File.Exists(item.ImageFileHolder.FullName))
continue;
if (!File.Exists(fullFileName))
continue;
File.Move(fullFileName, item.ImageFileHolder.FullName);
moved += 1;
}
_Logger?.LogInformation($"Done moving back {moved} file(s)");
for (int i = 1; i < 10; i++)
_ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(configuration.RootDirectory);
}
private static void CreateDateShortcut(Property.Models.Configuration configuration, Container[] containers)
{
string path;
string fileName;
string directory;
DateTime dateTime;
int selectedTotal;
const int minimum = 3;
List<DateTime> dateTimes;
List<Item> selectedItems;
const int maximumHours = 24;
string? relativePathDirectory;
WindowsShortcut windowsShortcut;
TimeSpan threeStandardDeviationHigh;
string aPropertyContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "()");
foreach (Container container in containers)
{
if (container.Items.Count == 0)
continue;
selectedTotal = 0;
threeStandardDeviationHigh = Shared.Models.Stateless.Methods.IProperty.GetThreeStandardDeviationHigh(minimum, container);
if (threeStandardDeviationHigh.TotalHours > maximumHours)
threeStandardDeviationHigh = new(maximumHours, 0, 0);
for (int i = 0; i < container.Items.Count; i++)
{
(i, dateTimes, selectedItems) = Shared.Models.Stateless.Methods.IProperty.Get(container, threeStandardDeviationHigh, i);
selectedTotal += selectedItems.Count;
foreach (Item item in selectedItems)
{
if (item.Property is null)
continue;
relativePathDirectory = Path.GetDirectoryName(item.RelativePath);
if (string.IsNullOrEmpty(relativePathDirectory))
continue;
if (item.ImageFileHolder.LastWriteTime is not null && item.Property is null)
dateTime = item.ImageFileHolder.LastWriteTime.Value;
else if (item.Property is not null && item.Property.DateTimeOriginal is not null)
dateTime = item.Property.DateTimeOriginal.Value;
else
dateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
path = Path.GetFullPath($"{configuration.RootDirectory}{item.RelativePath[..^5]}");
directory = Path.Combine($"{aPropertyContentDirectory}{relativePathDirectory}", $"{dateTimes.Min():yyyy-MM-dd_HH-mm-ss}---{dateTimes.Max():yyyy-MM-dd_HH-mm-ss}");
if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory);
fileName = Path.Combine(directory, $"{Path.GetFileName(item.RelativePath[..^5])}.lnk");
if (File.Exists(fileName))
continue;
windowsShortcut = new() { Path = path };
windowsShortcut.Save(fileName);
windowsShortcut.Dispose();
if (!File.Exists(fileName))
continue;
File.SetLastWriteTime(fileName, dateTime);
}
}
if (selectedTotal < container.Items.Count && selectedTotal < (from l in container.Items where l.Property is not null select true).Count())
continue;
}
}
}