WikiTree datasource refactoring

This commit is contained in:
Przemek Więch
2023-08-30 23:52:32 +02:00
parent bd6f6a845b
commit cb6b877734

View File

@@ -120,30 +120,21 @@ async function getRelatives(
return result.concat(response); return result.concat(response);
} }
/** async function logInIfNeeded(
* Loads data from WikiTree to populate an hourglass chart starting from the authcode: string | undefined,
* given person ID. handleCors: boolean,
*/ ) {
export async function loadWikiTree(
key: string,
intl: IntlShape,
authcode?: string,
): Promise<TopolaData> {
// Work around CORS if not in apps.wikitree.com domain.
const handleCors = window.location.hostname !== 'apps.wikitree.com';
if (!handleCors && !getLoggedInUserName() && authcode) { if (!handleCors && !getLoggedInUserName() && authcode) {
const loginResult = await clientLogin(authcode, {appId: WIKITREE_APP_ID}); const loginResult = await clientLogin(authcode, {appId: WIKITREE_APP_ID});
if (loginResult.result === 'Success') { if (loginResult.result === 'Success') {
sessionStorage.clear(); sessionStorage.clear();
} }
} }
}
const everyone: Person[] = []; async function getFirstPerson(key: string, handleCors: boolean) {
const person = (await getRelatives([key], handleCors))[0];
// Fetch the ancestors of the input person and ancestors of his/her spouses. if (!person?.Name) {
const firstPerson = await getRelatives([key], handleCors);
if (!firstPerson[0]?.Name) {
const id = key; const id = key;
throw new TopolaError( throw new TopolaError(
'WIKITREE_PROFILE_NOT_ACCESSIBLE', 'WIKITREE_PROFILE_NOT_ACCESSIBLE',
@@ -151,14 +142,16 @@ export async function loadWikiTree(
{id}, {id},
); );
} }
return person;
}
const spouseKeys = Object.values(firstPerson[0].Spouses || {}).map( function getSpouseKeys(person: Person) {
(s) => s.Name, return Object.values(person.Spouses || {}).map((s) => s.Name);
); }
async function getAllAncestors(keys: string[], handleCors: boolean) {
const ancestors = await Promise.all( const ancestors = await Promise.all(
[key] keys.map((key) => getAncestors(key, handleCors)),
.concat(spouseKeys)
.map((personId) => getAncestors(personId, handleCors)),
); );
const ancestorKeys = ancestors const ancestorKeys = ancestors
.flat() .flat()
@@ -204,11 +197,15 @@ export async function loadWikiTree(
person.Mother = privateMother; person.Mother = privateMother;
} }
}); });
everyone.push(...ancestorDetails);
// Collect private individuals. // Collect private individuals.
const privateAncestors = ancestors.flat().filter((person) => person.Id < 0); const privateAncestors = ancestors.flat().filter((person) => person.Id < 0);
everyone.push(...privateAncestors);
return ancestorDetails.concat(privateAncestors);
}
async function getAllDescendants(key: string, handleCors: boolean) {
const everyone: Person[] = [];
// Limit the number of generations of descendants because there may be tens of // Limit the number of generations of descendants because there may be tens of
// generations for some profiles. // generations for some profiles.
@@ -230,64 +227,84 @@ export async function loadWikiTree(
); );
generation++; generation++;
} }
return everyone;
}
//Map from human-readable person id to person names async function loadData(key: string, authcode?: string) {
const personNames = new Map< // Work around CORS if not in apps.wikitree.com domain.
string, const handleCors = window.location.hostname !== 'apps.wikitree.com';
{birth?: string; married?: string; aka?: string}
>();
await logInIfNeeded(authcode, handleCors);
const firstPerson = await getFirstPerson(key, handleCors);
const spouseKeys = getSpouseKeys(firstPerson);
// Fetch the ancestors of the input person and ancestors of his/her spouses.
const allAncestors = getAllAncestors([key].concat(spouseKeys), handleCors);
// Fetch descendants and their spouses.
const allDescendants = getAllDescendants(key, handleCors);
const everyone: Person[] = [
...(await allAncestors),
...(await allDescendants),
];
// Make sure the list contains unique elements.
return Array.from(
new Map(everyone.map((person) => [person.Id, person])).values(),
);
}
function getFamilies(people: Person[]) {
// Map from person id to the set of families where they are a spouse. // Map from person id to the set of families where they are a spouse.
const families = new Map<number, Set<string>>(); const families = new Map<number, Set<string>>();
// Map from family id to the set of children. people.forEach((person) => {
const children = new Map<string, Set<number>>();
// Map from famliy id to the spouses.
const spouses = new Map<
string,
{wife?: number; husband?: number; spouse?: Person}
>();
// Map from numerical id to human-readable id.
const idToName = new Map<number, string>();
// Map from human-readable person id to fullSizeUrl of person photo.
const fullSizePhotoUrls: Map<string, string> = new Map();
everyone.forEach((person) => {
idToName.set(person.Id, person.Name);
if (person.Mother || person.Father) { if (person.Mother || person.Father) {
const famId = getFamilyId(person.Mother, person.Father); const famId = getFamilyId(person.Mother, person.Father);
getSet(families, person.Mother).add(famId); getSet(families, person.Mother).add(famId);
getSet(families, person.Father).add(famId); getSet(families, person.Father).add(famId);
getSet(children, famId).add(person.Id);
spouses.set(famId, {
wife: person.Mother || undefined,
husband: person.Father || undefined,
});
} }
});
const indis: JsonIndi[] = [];
const converted = new Set<number>();
everyone.forEach((person) => {
if (converted.has(person.Id)) {
return;
}
converted.add(person.Id);
const indi = convertPerson(person, intl);
if (person.PhotoData?.path) {
fullSizePhotoUrls.set(
person.Name,
`https://www.wikitree.com${person.PhotoData.path}`,
);
}
personNames.set(person.Name, convertPersonNames(person));
if (person.Spouses) { if (person.Spouses) {
Object.values(person.Spouses).forEach((spouse) => { Object.values(person.Spouses).forEach((spouse) => {
const famId = getFamilyId(person.Id, spouse.Id); const famId = getFamilyId(person.Id, spouse.Id);
getSet(families, person.Id).add(famId); getSet(families, person.Id).add(famId);
getSet(families, spouse.Id).add(famId); getSet(families, spouse.Id).add(famId);
});
}
});
return families;
}
function getChildren(people: Person[]) {
// Map from family id to the set of children.
const children = new Map<string, Set<number>>();
people.forEach((person) => {
if (person.Mother || person.Father) {
const famId = getFamilyId(person.Mother, person.Father);
getSet(children, famId).add(person.Id);
}
});
return children;
}
function getSpouses(people: Person[]) {
// Map from famliy id to the spouses.
const spouses = new Map<
string,
{wife?: number; husband?: number; spouse?: Person}
>();
people.forEach((person) => {
if (person.Mother || person.Father) {
const famId = getFamilyId(person.Mother, person.Father);
spouses.set(famId, {
wife: person.Mother || undefined,
husband: person.Father || undefined,
});
}
if (person.Spouses) {
Object.values(person.Spouses).forEach((spouse) => {
const famId = getFamilyId(person.Id, spouse.Id);
const familySpouses = const familySpouses =
person.Gender === 'Male' person.Gender === 'Male'
? {wife: spouse.Id, husband: person.Id, spouse} ? {wife: spouse.Id, husband: person.Id, spouse}
@@ -295,11 +312,25 @@ export async function loadWikiTree(
spouses.set(famId, familySpouses); spouses.set(famId, familySpouses);
}); });
} }
indi.fams = Array.from(getSet(families, person.Id));
indis.push(indi);
}); });
return spouses;
}
const fams = Array.from(spouses.entries()).map(([key, value]) => { function convertIndis(people: Person[], intl: IntlShape) {
const families = getFamilies(people);
return people.map((person) => {
const indi = convertPerson(person, intl);
indi.fams = Array.from(getSet(families, person.Id));
return indi;
});
}
function convertFams(people: Person[]) {
// Map from numerical id to human-readable id.
const idToName = new Map(people.map((person) => [person.Id, person.Name]));
const children = getChildren(people);
const spouses = getSpouses(people);
return Array.from(spouses.entries()).map(([key, value]) => {
const fam: JsonFam = { const fam: JsonFam = {
id: key, id: key,
}; };
@@ -327,9 +358,34 @@ export async function loadWikiTree(
} }
return fam; return fam;
}); });
}
export async function loadWikiTree(
key: string,
intl: IntlShape,
authcode?: string,
): Promise<TopolaData> {
const everyone = await loadData(key, authcode);
const indis = convertIndis(everyone, intl);
const fams = convertFams(everyone);
const chartData = normalizeGedcom({indis, fams}); const chartData = normalizeGedcom({indis, fams});
//Map from human-readable person id to person names
const personNames = new Map(
everyone.map((person) => [person.Name, convertPersonNames(person)]),
);
// Map from human-readable person id to fullSizeUrl of person photo.
const fullSizePhotoUrls = new Map(
everyone
.filter((person) => person.PhotoData?.path)
.map((person) => [
person.Name,
`https://www.wikitree.com${person.PhotoData!.path}`,
]),
);
const gedcom = buildGedcom(chartData, fullSizePhotoUrls, personNames); const gedcom = buildGedcom(chartData, fullSizePhotoUrls, personNames);
return {chartData, gedcom}; return {chartData, gedcom};
} }
@@ -831,3 +887,4 @@ export class WikiTreeDataSource implements DataSource<WikiTreeSourceSpec> {
} }
} }
} }