mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-05-27 15:46:14 +00:00
WikiTree datasource refactoring
This commit is contained in:
@@ -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> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user