file-folder-helper/Helpers/HelperGenealogicalDataCommunication.cs
2024-12-14 09:41:18 -07:00

1015 lines
45 KiB
C#

using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
namespace File_Folder_Helper.Helpers;
internal static partial class HelperGenealogicalDataCommunication
{
private record Input(string GenealogicalDataCommunicationDirectory,
string? GenealogicalDataCommunicationFile,
string? SingletonDirectory,
string? Destination);
private record GenealogicalDataCommunicationCollections(ReadOnlyCollection<string> HeaderLines,
ReadOnlyDictionary<long, ReadOnlyCollection<string>> IndividualsToLines,
ReadOnlyCollection<ReadOnlyCollection<string>> FamilyGroupLines,
ReadOnlyCollection<string> FooterLines);
private record GenealogicalDataCommunicationRelation(int FamilyIndex,
string Relation,
int Id,
string? LineTwo);
private record PersonHour(char Status,
char Sex,
char First);
private record Collections(ReadOnlyDictionary<long, long> IdToPersonKey,
ReadOnlyDictionary<long, string> IdToName,
ReadOnlyDictionary<long, string> IdToGivenName,
ReadOnlyCollection<PersonExport> PersonExportCollection);
private record PersonExport(long Id,
ReadOnlyCollection<string> Lines,
string PersonKeyFormatted,
char[] AgeCollection,
DateTime DateTime,
long PersonKey);
private record Family(string? Title,
string? Id,
string? Index,
string PersonName,
Person Person,
long PersonKey,
string? LineTwo);
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ReadOnlyCollection<ReadOnlyCollection<string>>))]
internal partial class CollectionSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ReadOnlyDictionary<string, ReadOnlyCollection<ReadOnlyCollection<string>>>))]
internal partial class DictionarySourceGenerationContext : JsonSerializerContext
{
}
private static string? GetFaceBook(Person person) =>
person.Birth?.Continue.Where(l => !l.Contains("profile.php?id=") && l.StartsWith("https://www.facebook.com/")).Select(l => l[25..].Split('/')[0]).FirstOrDefault();
private static string? GetFaceBookId(Person person) =>
person.Birth?.Continue.Where(l => l.StartsWith("https://www.facebook.com/profile.php?id=")).Select(l => l[40..].Split('&')[0]).FirstOrDefault();
private static string GetKey(Family family) =>
$"{family.Id}-{family.Index}".Trim('-');
private static ReadOnlyDictionary<long, ReadOnlyCollection<string>> Convert(Dictionary<long, List<string>> keyValuePairs)
{
Dictionary<long, ReadOnlyCollection<string>> results = [];
foreach (KeyValuePair<long, List<string>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, new(keyValuePair.Value));
return new(results);
}
private static Dictionary<string, ReadOnlyCollection<ReadOnlyCollection<string>>> Convert(Dictionary<string, List<ReadOnlyCollection<string>>> keyValuePairs)
{
Dictionary<string, ReadOnlyCollection<ReadOnlyCollection<string>>> results = [];
foreach (KeyValuePair<string, List<ReadOnlyCollection<string>>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, new(keyValuePair.Value));
return new(results);
}
private static ReadOnlyCollection<string> GetObjectCollection(Person? person)
{
List<string> results;
if (person is null)
results = ["Id", "First-Name", "Last-Name", "Birth-Date", "Sex", "Address", "City", "State", "Zip", "Phone", "E-mail", "Facebook", "Facebook-Id", "Comment", "U-Id"];
else
{
string? facebook = GetFaceBook(person);
string? facebookId = GetFaceBookId(person);
results =
[
person.Id.ToString(),
string.Concat(person.Name?.Given),
string.Concat(person.Name?.Sur),
string.Concat(person.Birth?.Date.ToString()),
string.Concat(person.Sex),
string.Empty,
string.Empty,
"NM",
string.Empty,
string.Empty,
string.Empty,
string.Concat(facebook),
string.Concat(facebookId),
string.Empty,
string.Concat(person.UId)
];
}
return new(results);
}
private static ReadOnlyCollection<string> GetDistinctSortedKeys(List<Family> familyCollection, char personTitleFilter)
{
string[] results;
string key;
List<(string? Index, string Key)> collection = [];
foreach (Family family in familyCollection)
{
if (family.Id is null)
continue;
if (string.IsNullOrEmpty(family.Title) || family.Title[0] != personTitleFilter)
continue;
key = GetKey(family);
collection.Add((family.Index, key));
}
results = (from l in collection orderby l.Key, l.Index?.Length descending select l.Key).Distinct().ToArray();
return new(results);
}
private static Dictionary<string, List<ReadOnlyCollection<string>>> Convert(ReadOnlyCollection<string> distinctSortedKKeys)
{
Dictionary<string, List<ReadOnlyCollection<string>>> results = [];
ReadOnlyCollection<string> collection;
List<ReadOnlyCollection<string>>? objectCollection;
foreach (string key in distinctSortedKKeys)
{
if (results.ContainsKey(key))
continue;
if (!results.TryGetValue(key, out objectCollection))
{
results.Add(key, []);
if (!results.TryGetValue(key, out objectCollection))
throw new NotSupportedException();
}
collection = GetObjectCollection(person: null);
objectCollection.Add(collection);
}
return results;
}
private static ReadOnlyCollection<string> GetHeaderLines(string startsWith, string[] sourceLines)
{
List<string> results = [];
for (int i = 0; i < sourceLines.Length; i++)
{
if (sourceLines[i].StartsWith(startsWith))
break;
results.Add(sourceLines[i]);
}
return new(results);
}
private static long? GetId(string line)
{
long? result;
string[] segments = line.Split('@');
result = segments[1].Length < 2 || !long.TryParse(segments[1][1..], out long idValue) ? null : idValue;
return result;
}
private static Dictionary<string, List<string>> GetTxtFileCollection(Input input)
{
Dictionary<string, List<string>> results = [];
string[] lines;
string[] directories;
string directoryName;
string? sourceDirectory;
string? parentDirectory;
List<string>? collectionA;
List<string>? collectionB;
string siblingDirectoryName;
string[] files = input.SingletonDirectory is null || !Directory.Exists(input.SingletonDirectory) ? [] : Directory.GetFiles(input.SingletonDirectory, "*.txt", SearchOption.AllDirectories);
foreach (string file in files)
{
sourceDirectory = Path.GetDirectoryName(file);
if (sourceDirectory is null)
continue;
parentDirectory = Path.GetDirectoryName(sourceDirectory);
if (parentDirectory is null)
continue;
lines = File.ReadAllLines(file);
if (lines.Length != 1 || lines.Length == 2 && string.IsNullOrEmpty(lines[1]))
continue;
directoryName = Path.GetFileName(sourceDirectory);
if (!results.TryGetValue(directoryName, out collectionA))
{
results.Add(directoryName, []);
if (!results.TryGetValue(directoryName, out collectionA))
throw new Exception();
}
collectionA.Add(lines[0]);
directories = Directory.GetDirectories(parentDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories)
{
siblingDirectoryName = Path.GetFileName(directory);
collectionA.Add(siblingDirectoryName);
}
foreach (string directory in directories)
{
siblingDirectoryName = Path.GetFileName(directory);
if (!results.TryGetValue(siblingDirectoryName, out collectionB))
{
results.Add(siblingDirectoryName, []);
if (!results.TryGetValue(siblingDirectoryName, out collectionB))
throw new Exception();
}
collectionB.AddRange(collectionA);
collectionB.Add(lines[0]);
}
}
return results;
}
private static (int, Name) GetName(ReadOnlyCollection<string> lines, int i)
{
Name result;
string seven;
string? surName = null;
string? nickName = null;
string? givenName = null;
string? suffixName = null;
string? forwardSlashFullName = lines[i][..7] == "1 NAME " ? lines[i][7..] : null;
for (int j = i + 1; j < lines.Count; j++)
{
if (lines[j][0] == '1')
break;
i++;
if (lines[j].Length < 7)
throw new NotImplementedException();
seven = lines[j][..7];
if (seven == "2 GIVN ")
givenName = lines[j][7..];
else if (seven == "2 SURN ")
surName = lines[j][7..];
else if (seven == "2 NICK ")
nickName = lines[j][7..];
else if (seven == "2 NSFX ")
suffixName = lines[j][7..];
else
throw new NotImplementedException();
}
result = new(forwardSlashFullName, givenName, surName, nickName, suffixName);
return (i, result);
}
private static (int, Birth, bool) GetBirth(Dictionary<string, List<string>> keyValuePairs, ReadOnlyCollection<string> lines, int i)
{
Birth result;
string seven;
string? note = null;
bool moreAdded = false;
List<string>? collection;
DateOnly? dateOnly = null;
List<string> distinct = [];
List<string> @continue = [];
for (int j = i + 1; j < lines.Count; j++)
{
if (lines[j][0] == '1')
break;
i++;
if (lines[j].Length < 7)
throw new NotImplementedException();
seven = lines[j][..7];
if (seven == "2 DATE ")
dateOnly = !DateOnly.TryParseExact(lines[j][7..], "d MMM yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateOnly dateOnlyValue) ? null : dateOnlyValue;
else if (seven == "2 NOTE ")
note = lines[j][7..];
else if (seven == "3 CONT ")
{
for (int k = j; k < lines.Count; k++)
{
if (lines[k][0] == '1')
break;
seven = lines[k][..7];
if (seven != "3 CONT ")
break;
j++;
@continue.Add(lines[k][7..]);
}
if (note is not null && keyValuePairs.TryGetValue(note, out collection))
{
distinct.Add(note);
distinct.AddRange(@continue);
foreach (string text in collection)
{
if (distinct.Contains(text))
continue;
distinct.Add(text);
if (!moreAdded)
moreAdded = true;
@continue.Add(text);
}
}
}
else
continue;
}
result = new(dateOnly, note, @continue);
return (i, result, moreAdded);
}
private static (int, Death) GetDeath(ReadOnlyCollection<string> lines, int i)
{
Death result;
string seven;
string? note = null;
DateOnly? dateOnly = null;
List<string> @continue = [];
bool? isDead = lines[i].Length == 8 && lines[i][..8] == "1 DEAT Y" ? true : lines[i].Length == 8 && lines[i][..8] == "1 DEAT N" ? false : null;
for (int j = i + 1; j < lines.Count; j++)
{
if (lines[j][0] == '1')
break;
i++;
if (lines[j].Length < 7)
throw new NotImplementedException();
seven = lines[j][..7];
if (seven == "2 DATE ")
dateOnly = !DateOnly.TryParseExact(lines[j][7..], "d MMM yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateOnly dateOnlyValue) ? null : dateOnlyValue;
else if (seven == "2 NOTE ")
note = lines[j][7..];
else if (seven == "3 CONT ")
{
for (int k = j; k < lines.Count; k++)
{
if (lines[k][0] == '1')
break;
seven = lines[k][..7];
if (seven != "3 CONT ")
break;
j++;
@continue.Add(lines[k][7..]);
}
}
else
throw new NotImplementedException();
}
result = new(dateOnly is not null ? true : isDead, dateOnly, note, @continue);
return (i, result);
}
private static (int, Change) GetChange(ReadOnlyCollection<string> lines, int i)
{
Change result;
string seven;
string? note = null;
DateOnly? dateOnly = null;
List<string> @continue = [];
for (int j = i + 1; j < lines.Count; j++)
{
if (lines[j][0] == '1')
break;
i++;
if (lines[j].Length < 7)
throw new NotImplementedException();
seven = lines[j][..7];
if (seven == "2 DATE ")
dateOnly = !DateOnly.TryParseExact(lines[j][7..], "d MMM yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateOnly dateOnlyValue) ? null : dateOnlyValue;
else if (seven == "2 NOTE ")
note = lines[j][7..];
else if (seven == "3 CONT ")
{
for (int k = j; k < lines.Count; k++)
{
if (lines[k][0] == '1')
break;
seven = lines[k][..7];
if (seven != "3 CONT ")
break;
j++;
@continue.Add(lines[k][7..]);
}
}
else
throw new NotImplementedException();
}
result = new(dateOnly, note, @continue);
return (i, result);
}
private static string[] GetNewLines(ReadOnlyCollection<string> lines, Birth? birth)
{
List<string> results = [];
string six;
string text;
string seven;
List<string> @continue = birth is null ? [] : birth.Continue.ToList();
for (int i = 0; i < lines.Count; i++)
{
if (birth is null)
throw new NotSupportedException();
if (lines[i].Length < 6)
throw new NotImplementedException();
results.Add(lines[i]);
six = lines[i][..6];
if (lines[i][0] == '1')
{
if (six != "1 BIRT")
continue;
for (int j = i + 1; j < lines.Count; j++)
{
if (lines[j].Length < 7)
throw new NotImplementedException();
if (lines[j][0] == '1')
break;
i++;
seven = lines[j][..7];
if (seven != "3 CONT ")
results.Add(lines[j]);
else
{
text = lines[j][7..];
if (@continue.Contains(text))
{
results.Add(lines[j]);
_ = @continue.Remove(text);
}
}
}
results.AddRange(from l in @continue orderby l select $"3 CONT {l}");
}
}
return results.ToArray();
}
[GeneratedRegex("[\\\\,\\/,\\:,\\*,\\?,\\\",\\<,\\>,\\|]")]
private static partial Regex WindowsFileSystem();
private static List<long> GetIdsWhenPersonHasTitle(ReadOnlyDictionary<long, Person> people)
{
List<long> results = [];
foreach (KeyValuePair<long, Person> keyValuePair in people)
{
if (keyValuePair.Value.Title is null)
continue;
results.Add(keyValuePair.Key);
}
return results;
}
private static ReadOnlyCollection<GenealogicalDataCommunicationRelation> GetRelations(ReadOnlyCollection<ReadOnlyCollection<string>> familyGroupLines)
{
List<GenealogicalDataCommunicationRelation> results = [];
int id;
string relation;
string[] segments;
ReadOnlyCollection<string> familyLines;
for (int i = 0; i < familyGroupLines.Count; i++)
{
familyLines = familyGroupLines[i];
for (int j = 0; j < familyLines.Count; j++)
{
segments = familyLines[j].Split('@');
if (segments[0].Length < 3 || segments.Length != 3)
continue;
if (!int.TryParse(segments[1][1..], out id))
continue;
relation = segments[0][2..].Trim();
if (j + 1 >= familyLines.Count || familyLines[j + 1].Length < 3 || familyLines[j + 1][..3] != "2 _")
results.Add(new(i, relation, id, null));
else
results.Add(new(i, relation, id, familyLines[j + 1][2..]));
}
}
return new(results.OrderBy(l => l.FamilyIndex).ToArray());
}
private static ReadOnlyDictionary<string, ReadOnlyCollection<ReadOnlyCollection<string>>> GetKeyValuePairs(List<Family> familyCollection, char personTitleFilter)
{
Dictionary<string, ReadOnlyCollection<ReadOnlyCollection<string>>> results;
Dictionary<string, List<ReadOnlyCollection<string>>> keyValuePairs;
string id;
string key;
ReadOnlyCollection<string> collection;
List<ReadOnlyCollection<string>>? objectCollection;
ReadOnlyCollection<string> distinctSortedKeys = GetDistinctSortedKeys(familyCollection, personTitleFilter);
keyValuePairs = Convert(distinctSortedKeys);
foreach (Family family in familyCollection)
{
if (family.Id is null)
continue;
if (string.IsNullOrEmpty(family.Title) || family.Title[0] != personTitleFilter)
continue;
id = family.Person.Id.ToString();
key = GetKey(family);
if (!keyValuePairs.TryGetValue(key, out objectCollection))
throw new NotSupportedException();
collection = GetObjectCollection(family.Person);
objectCollection.Add(collection);
}
results = Convert(keyValuePairs);
return new(results);
}
private static string GetHourGroup(string personDisplayDirectoryName, int hour) =>
hour == 0 ? "Unknown-Unknown-Unknown" :
hour == 1 ? "Unknown-Unknown-Unknown" :
hour == 2 ? "Unknown-Unknown-Unknown" :
hour == 3 ? "Alive-Unknown-Yes" :
hour == 4 ? "Alive-Female-Yes" :
hour == 5 ? "Alive-Male-Yes" :
hour == 6 ? "Alive-Female-No" :
hour == 7 ? "Alive-Male-No" :
hour == 13 ? "Dead-Unknown-Yes" :
hour == 14 ? "Dead-Female-Yes" :
hour == 15 ? "Dead-Male-Yes" :
hour == 16 ? "Dead-Female-No" :
hour == 17 ? "Dead-Male-No" :
throw new NotImplementedException(personDisplayDirectoryName);
private static (int, TimeSpan) GetAge(long minuendTicks, long subtrahendTicks)
{
TimeSpan result;
int years = 0;
DateTime check = new(subtrahendTicks);
for (int i = 0; i < int.MaxValue; i++)
{
check = check.AddYears(1);
if (check.Ticks > minuendTicks)
break;
years += 1;
}
result = new(minuendTicks - check.AddYears(-1).Ticks);
return (years, result);
}
private static string? GetYearGroup(string year) =>
!int.TryParse(year[2..], out int part) ? null : string.Concat(year[..^2], part < 50 ? "--" : "++");
private static Input GetInput(List<string> args)
{
Input result;
string? destination = null;
string? singletonDirectory = null;
string genealogicalDataCommunicationRootDirectory = Path.GetFullPath(args[0]);
string fileName = Path.GetFileName(genealogicalDataCommunicationRootDirectory);
string[] files = Directory.GetFiles(genealogicalDataCommunicationRootDirectory, $"{fileName}.ged", SearchOption.TopDirectoryOnly);
string genealogicalDataCommunicationDirectory = Path.Combine(genealogicalDataCommunicationRootDirectory, fileName);
if (!Directory.Exists(genealogicalDataCommunicationDirectory))
_ = Directory.CreateDirectory(genealogicalDataCommunicationDirectory);
for (int i = 1; i < args.Count; i++)
{
if (args[i].Length == 2 && i + 1 < args.Count)
{
if (args[i][1] == 's')
singletonDirectory = Path.GetFullPath(args[i + 1]);
else if (args[i][1] == 'd')
destination = Path.GetFullPath(args[i + 1]);
i++;
}
}
string? genealogicalDataCommunicationFile = files.Length != 1 ? null : files[0];
if (destination is not null)
{
string? root = Path.GetPathRoot(destination);
if (root is null || !Directory.Exists(root))
throw new NotSupportedException($"This method requires frontMatterYamlLines valid -d path <{root}>!");
if (!Directory.Exists(destination))
_ = Directory.CreateDirectory(destination);
}
result = new(genealogicalDataCommunicationDirectory, genealogicalDataCommunicationFile, singletonDirectory, destination);
return result;
}
private static GenealogicalDataCommunicationCollections GetGenealogicalDataCommunicationCollections(Input input)
{
GenealogicalDataCommunicationCollections result;
long? id;
List<string> lines = [];
List<string> footerLines = [];
const string startsWith = "0 @";
Dictionary<long, List<string>> keyValuePairs = [];
List<ReadOnlyCollection<string>> familyGroupLines = [];
string[] sourceLines = string.IsNullOrEmpty(input.GenealogicalDataCommunicationFile) || !File.Exists(input.GenealogicalDataCommunicationFile) ? [] : File.ReadAllLines(input.GenealogicalDataCommunicationFile);
ReadOnlyCollection<string> headerLines = GetHeaderLines(startsWith, sourceLines);
for (int i = headerLines.Count; i < sourceLines.Length; i++)
{
if (!sourceLines[i].StartsWith(startsWith))
continue;
if (sourceLines[i].EndsWith("@ SOUR") || sourceLines[i].EndsWith("@ SUBM") || sourceLines[i].EndsWith("@ OBJE") || sourceLines[i].EndsWith("@ REPO"))
continue;
lines.Add(sourceLines[i]);
if (sourceLines[i].EndsWith("@ FAM"))
{
lines.Clear();
for (int j = sourceLines.Length - 1; j >= i; j--)
{
lines.Add(sourceLines[j]);
if (sourceLines[j][0] == '0')
{
if (!sourceLines[j].EndsWith("@ FAM"))
footerLines.AddRange(lines);
else
{
lines.Reverse();
familyGroupLines.Add(new(lines.ToArray()));
}
lines.Clear();
}
}
familyGroupLines.Reverse();
footerLines.Reverse();
break;
}
else if (sourceLines[i].EndsWith("@ INDI"))
{
id = GetId(sourceLines[i]);
for (int j = i + 1; j < sourceLines.Length; j++)
{
if (sourceLines[j].StartsWith(startsWith))
break;
lines.Add(sourceLines[j]);
}
if (id is null)
throw new Exception(string.Join(Environment.NewLine, lines));
keyValuePairs.Add(id.Value, []);
if (lines.Count == 0)
continue;
keyValuePairs[id.Value].AddRange(lines);
lines.Clear();
}
else
throw new NotSupportedException();
}
ReadOnlyDictionary<long, ReadOnlyCollection<string>> individualsToLines = Convert(keyValuePairs);
result = new(headerLines, individualsToLines, new(familyGroupLines), new(footerLines));
return result;
}
private static ReadOnlyDictionary<long, Person> GetPeople(Input input, GenealogicalDataCommunicationCollections genealogicalDataCommunicationCollections)
{
Dictionary<long, Person> results = [];
long? id;
char? sex;
Name? name;
string six;
string? uId;
Birth? birth;
Death? death;
Person person;
string? title;
Change? change;
bool? moreAdded;
ReadOnlyCollection<string> lines;
Dictionary<string, List<string>> keyValuePairs = GetTxtFileCollection(input);
foreach (KeyValuePair<long, ReadOnlyCollection<string>> keyValuePair in genealogicalDataCommunicationCollections.IndividualsToLines)
{
id = null;
sex = null;
name = null;
uId = null;
birth = null;
death = null;
title = null;
change = null;
moreAdded = null;
lines = keyValuePair.Value;
for (int i = 0; i < lines.Count; i++)
{
if (lines[i].Length < 6)
throw new NotImplementedException();
six = lines[i][..6];
if (lines[i][0] == '0')
{
if (lines[i][^6..] == "@ INDI")
id = GetId(lines[i]);
else
throw new NotImplementedException();
}
else if (lines[i][0] == '1')
{
if (six == "1 NAME")
(i, name) = GetName(lines, i);
else if (six == "1 SEX ")
sex = lines[i].Length != 7 ? null : lines[i][6];
else if (six is "1 UID " or "1 _UID")
uId = lines[i].Length == 6 ? null : lines[i][7..];
else if (six == "1 BIRT")
(i, birth, moreAdded) = GetBirth(keyValuePairs, lines, i);
else if (six == "1 DEAT")
(i, death) = GetDeath(lines, i);
else if (six == "1 TITL")
title = lines[i].Length == 6 ? null : lines[i][7..];
else if (six == "1 CHAN")
(i, change) = GetChange(lines, i);
else if (six is "1 FAMC" or "1 FAMS")
continue;
else
throw new NotImplementedException();
}
}
if (id is null)
throw new Exception(string.Join(Environment.NewLine, lines));
if (moreAdded is null || !moreAdded.Value)
person = new(id.Value, name, sex, uId, birth, title, death, change, lines.ToArray());
else
person = new(id.Value, name, sex, uId, birth, title, death, change, GetNewLines(lines, birth));
results.Add(id.Value, person);
}
return new(results);
}
private static Collections GetCollections(AppSettings appSettings, ReadOnlyDictionary<long, Person> people)
{
Collections result;
long personKey;
char[] ageCollection;
List<PersonExport> collection = [];
Dictionary<long, string> idToName = [];
Dictionary<long, long> idToPersonKey = [];
Dictionary<long, string> idToGivenName = [];
int length = appSettings.PersonBirthdayFormat.Length;
foreach (KeyValuePair<long, Person> keyValuePair in people)
{
if (keyValuePair.Value.Birth?.Note is null)
continue;
if (string.IsNullOrEmpty(keyValuePair.Value.Name?.ForwardSlashFull))
continue;
if (keyValuePair.Value.Birth.Note.Length != length)
continue;
if (!DateTime.TryParseExact(keyValuePair.Value.Birth.Note, appSettings.PersonBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateTime))
continue;
personKey = dateTime.Ticks;
idToPersonKey.Add(keyValuePair.Key, personKey);
idToName.Add(keyValuePair.Key, WindowsFileSystem().Replace(keyValuePair.Value.Name.ForwardSlashFull, "_"));
ageCollection = keyValuePair.Value.Birth.Continue.Count == 0 ? [] : keyValuePair.Value.Birth.Continue[0].ToArray();
idToGivenName.Add(keyValuePair.Key, string.IsNullOrEmpty(keyValuePair.Value.Name.Given) ? WindowsFileSystem().Replace(keyValuePair.Value.Name.ForwardSlashFull, "_") : WindowsFileSystem().Replace(keyValuePair.Value.Name.Given, "_"));
collection.Add(new(keyValuePair.Key, new(keyValuePair.Value.Lines), keyValuePair.Value.Birth.Note, ageCollection, dateTime, personKey));
}
result = new(new(idToPersonKey), new(idToName), new(idToGivenName), new(collection));
return result;
}
private static List<Family> GetFamilyCollection(ReadOnlyCollection<ReadOnlyCollection<string>> familyGroupLines, ReadOnlyDictionary<long, Person> people, ReadOnlyDictionary<long, long> idToPersonKey, ReadOnlyDictionary<long, string> idToName, ReadOnlyDictionary<long, string> idToGivenName)
{
List<Family> results = [];
string? name;
long personKey;
Person? person;
string? givenName;
string familyIndex;
const string wife = "WIFE";
string? familyTitle = null;
const string child = "CHIL";
const string husband = "HUSB";
string wifeName = string.Empty;
string? lastFamilyIndex = null;
string husbandName = string.Empty;
List<long> family = GetIdsWhenPersonHasTitle(people);
ReadOnlyCollection<GenealogicalDataCommunicationRelation> genealogicalDataCommunicationRelations = GetRelations(familyGroupLines);
foreach (GenealogicalDataCommunicationRelation genealogicalDataCommunicationRelation in genealogicalDataCommunicationRelations)
{
if (idToName.Count == 0 || idToGivenName.Count == 0)
break;
if (!idToName.TryGetValue(genealogicalDataCommunicationRelation.Id, out name))
continue;
if (!people.TryGetValue(genealogicalDataCommunicationRelation.Id, out person))
continue;
if (!idToGivenName.TryGetValue(genealogicalDataCommunicationRelation.Id, out givenName))
continue;
familyIndex = genealogicalDataCommunicationRelation.FamilyIndex.ToString("0000");
if (lastFamilyIndex is not null && lastFamilyIndex != familyIndex)
{
familyTitle = null;
wifeName = string.Empty;
husbandName = string.Empty;
}
lastFamilyIndex = familyIndex;
if (genealogicalDataCommunicationRelation.Relation == husband)
{
husbandName = givenName;
if (person.Title is not null)
familyTitle = person.Title;
continue;
}
if (genealogicalDataCommunicationRelation.Relation == wife)
{
wifeName = givenName;
if (person.Title is not null)
familyTitle = person.Title;
continue;
}
if (genealogicalDataCommunicationRelation.Relation != child)
continue;
if (!idToPersonKey.TryGetValue(genealogicalDataCommunicationRelation.Id, out personKey))
continue;
if (person.Title is not null)
familyTitle = person.Title;
_ = family.Remove(genealogicalDataCommunicationRelation.Id);
results.Add(new(familyTitle, $"{husbandName}-{wifeName}".Trim('-'), familyIndex, name, person, personKey, genealogicalDataCommunicationRelation.LineTwo));
}
foreach (KeyValuePair<long, Person> keyValuePair in people)
{
if (!family.Contains(keyValuePair.Key))
continue;
if (!idToName.TryGetValue(keyValuePair.Key, out name))
continue;
if (!idToPersonKey.TryGetValue(keyValuePair.Key, out personKey))
continue;
if (!idToGivenName.TryGetValue(keyValuePair.Key, out givenName))
continue;
if (!family.Remove(keyValuePair.Key))
continue;
results.Add(new(keyValuePair.Value.Title, givenName, null, name, keyValuePair.Value, personKey, null));
}
if (family.Count > 0)
throw new NotSupportedException();
foreach (GenealogicalDataCommunicationRelation genealogicalDataCommunicationRelation in genealogicalDataCommunicationRelations)
{
if (idToName.Count == 0 || idToGivenName.Count == 0)
break;
if (!idToName.TryGetValue(genealogicalDataCommunicationRelation.Id, out name))
continue;
if (!people.TryGetValue(genealogicalDataCommunicationRelation.Id, out person))
continue;
if (!idToPersonKey.TryGetValue(genealogicalDataCommunicationRelation.Id, out personKey))
continue;
results.Add(new(null, null, null, name, person, personKey, genealogicalDataCommunicationRelation.LineTwo));
}
return results;
}
private static void WriteJsonFiles(AppSettings appSettings, Input input, GenealogicalDataCommunicationCollections genealogicalDataCommunicationCollections, ReadOnlyDictionary<long, Person> people, List<Family> familyCollection)
{
string json;
if (people.Count != genealogicalDataCommunicationCollections.IndividualsToLines.Count)
throw new NotSupportedException();
ReadOnlyDictionary<string, ReadOnlyCollection<ReadOnlyCollection<string>>> keyValuePairs;
json = JsonSerializer.Serialize(new(people), PeopleSourceGenerationContext.Default.DictionaryInt64Person);
File.WriteAllText(Path.Combine(input.GenealogicalDataCommunicationDirectory, "people.json"), json);
Dictionary<long, Person>? result = JsonSerializer.Deserialize(json, PeopleSourceGenerationContext.Default.DictionaryInt64Person);
if (result is null)
throw new NullReferenceException(nameof(result));
json = JsonSerializer.Serialize(genealogicalDataCommunicationCollections.FamilyGroupLines, CollectionSourceGenerationContext.Default.ReadOnlyCollectionReadOnlyCollectionString);
File.WriteAllText(Path.Combine(input.GenealogicalDataCommunicationDirectory, "family.json"), json);
foreach (char personTitleFilter in appSettings.PersonTitleFilters)
{
keyValuePairs = GetKeyValuePairs(familyCollection, personTitleFilter);
json = JsonSerializer.Serialize(keyValuePairs, DictionarySourceGenerationContext.Default.ReadOnlyDictionaryStringReadOnlyCollectionReadOnlyCollectionString);
File.WriteAllText(Path.Combine(input.GenealogicalDataCommunicationDirectory, $"{personTitleFilter}.json"), json);
}
}
private static void WriteGenealogicalDataCommunicationCollections(ILogger<Worker> logger, Input input, GenealogicalDataCommunicationCollections genealogicalDataCommunicationCollections, ReadOnlyDictionary<long, Person> people)
{
List<string> lines = [];
List<string> allLines = [];
if (genealogicalDataCommunicationCollections.HeaderLines.Count > 0)
{
allLines.AddRange(genealogicalDataCommunicationCollections.HeaderLines);
File.WriteAllLines(Path.Combine(input.GenealogicalDataCommunicationDirectory, "a.pged"), genealogicalDataCommunicationCollections.HeaderLines);
}
if (people.Count > 0)
{
lines.Clear();
ReadOnlyCollection<string>? collection;
foreach (KeyValuePair<long, Person> keyValuePair in people)
{
if (!genealogicalDataCommunicationCollections.IndividualsToLines.TryGetValue(keyValuePair.Key, out collection))
throw new NotSupportedException();
if (keyValuePair.Value.Lines.Length != collection.Count)
logger.LogInformation("{name} has been changed", keyValuePair.Value.Name?.ForwardSlashFull);
lines.AddRange(keyValuePair.Value.Lines);
}
allLines.AddRange(lines);
File.WriteAllLines(Path.Combine(input.GenealogicalDataCommunicationDirectory, "b.pged"), lines);
}
if (genealogicalDataCommunicationCollections.FamilyGroupLines.Count > 0)
{
lines.Clear();
foreach (ReadOnlyCollection<string> keyValuePair in genealogicalDataCommunicationCollections.FamilyGroupLines)
lines.AddRange(keyValuePair);
allLines.AddRange(lines);
File.WriteAllLines(Path.Combine(input.GenealogicalDataCommunicationDirectory, "c.pged"), lines);
}
if (genealogicalDataCommunicationCollections.FooterLines.Count > 0)
{
allLines.AddRange(genealogicalDataCommunicationCollections.FooterLines);
File.WriteAllLines(Path.Combine(input.GenealogicalDataCommunicationDirectory, "d.pged"), genealogicalDataCommunicationCollections.FooterLines);
}
File.WriteAllLines(Path.Combine(input.GenealogicalDataCommunicationDirectory, "e.ged"), allLines);
}
private static void ExportFamilies(AppSettings appSettings, Input input, List<Family> familyCollection)
{
string directory;
DateTime dateTime;
string? destinationRoot;
string destinationDirectory;
foreach (Family family in familyCollection)
{
destinationRoot = Path.GetDirectoryName(input.GenealogicalDataCommunicationFile);
if (string.IsNullOrEmpty(destinationRoot))
continue;
dateTime = new(family.PersonKey);
directory = family.Id is null ? "A-A-0000" : $"{family.Title ?? "O"}-{family.Id}-{family.Index}".Trim('-');
destinationDirectory = Path.Combine(destinationRoot, directory, family.PersonName, dateTime.ToString(appSettings.PersonBirthdayFormat));
if (!Directory.Exists(destinationDirectory))
_ = Directory.CreateDirectory(destinationDirectory);
File.WriteAllText(Path.Combine(destinationDirectory, $"{family.PersonName}.txt"), family.LineTwo);
}
}
private static void Export(Input input, long ticks, ReadOnlyDictionary<long, string> idToName, ReadOnlyCollection<PersonExport> personExportCollection)
{
int age;
string text;
string? name;
string directory;
string hourGroup;
string? yearGroup;
long count = ticks;
string rootDirectory;
string approximateYears;
List<string> distinct = [];
List<string> duplicates = [];
string personDisplayDirectoryName;
foreach (PersonExport personExport in personExportCollection)
{
if (input.Destination is null)
break;
if (!idToName.TryGetValue(personExport.Id, out name))
continue;
hourGroup = GetHourGroup(name, personExport.DateTime.Hour);
(age, _) = GetAge(DateTime.Now.Ticks, personExport.PersonKey);
for (int i = 1; i < 3; i++)
{
if (i == 2)
{
yearGroup = GetYearGroup(personExport.DateTime.Year.ToString());
personDisplayDirectoryName = name;
if (string.IsNullOrEmpty(yearGroup))
continue;
}
else if (i == 1)
{
yearGroup = personExport.AgeCollection[0].ToString();
approximateYears = yearGroup[0] == '^' ? $"^{age}" : new string(personExport.AgeCollection);
personDisplayDirectoryName = $"{name}{approximateYears}";
if (distinct.Contains(personDisplayDirectoryName))
{
duplicates.Add(personDisplayDirectoryName);
continue;
}
distinct.Add(personDisplayDirectoryName);
}
else
throw new NotSupportedException();
rootDirectory = i == 1 ? input.Destination : i == 2 ? input.GenealogicalDataCommunicationDirectory : throw new NotSupportedException();
directory = Path.Combine(rootDirectory, yearGroup, hourGroup, personDisplayDirectoryName, personExport.PersonKeyFormatted);
if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory);
if (i == 2)
{
text = string.Join(Environment.NewLine, personExport.Lines);
count += 1;
File.WriteAllText(Path.Combine(directory, $"{count}-A.pged"), text);
}
text = string.Join(Environment.NewLine, personExport.Lines);
if (!string.IsNullOrEmpty(text))
{
count += 1;
if (i == 2)
File.WriteAllText(Path.Combine(directory, $"{count}-B.pged"), text);
else
File.WriteAllText(Path.Combine(directory, $"{personExport.PersonKeyFormatted}.pged"), text);
}
}
}
if (duplicates.Count > 0)
throw new NotSupportedException();
}
internal static void FileSystemToGenealogicalDataCommunication(AppSettings appSettings, ILogger<Worker> logger, List<string> args)
{
Input input = GetInput(args);
long ticks = DateTime.Now.Ticks;
logger.LogInformation("{ticks}", ticks);
logger.LogInformation("{old} {days} day(s) => {new}", 638258293638438812, 4, new DateTime(638258293638438812).AddDays(4.0001).Ticks);
GenealogicalDataCommunicationCollections genealogicalDataCommunicationCollections = GetGenealogicalDataCommunicationCollections(input);
ReadOnlyDictionary<long, Person> people = GetPeople(input, genealogicalDataCommunicationCollections);
Collections collections = GetCollections(appSettings, people);
if (collections.IdToPersonKey.Count != people.Count || collections.IdToPersonKey.Count != collections.IdToName.Count || collections.IdToPersonKey.Count != collections.IdToGivenName.Count)
throw new NotSupportedException();
List<Family> familyCollection = GetFamilyCollection(genealogicalDataCommunicationCollections.FamilyGroupLines, people, collections.IdToPersonKey, collections.IdToName, collections.IdToGivenName);
WriteJsonFiles(appSettings, input, genealogicalDataCommunicationCollections, people, familyCollection);
WriteGenealogicalDataCommunicationCollections(logger, input, genealogicalDataCommunicationCollections, people);
if (input.Destination is not null)
ExportFamilies(appSettings, input, familyCollection);
if (input.Destination is not null)
Export(input, ticks, collections.IdToName, collections.PersonExportCollection);
if (string.IsNullOrEmpty(input.GenealogicalDataCommunicationFile))
logger.LogInformation("{file} is null?", input.GenealogicalDataCommunicationDirectory);
}
}