Skip to content

Commit

Permalink
Fixes #104: Fix name mismatch
Browse files Browse the repository at this point in the history
  • Loading branch information
magicsunday committed Apr 2, 2024
1 parent 3e0367c commit 4e57434
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 26 deletions.
2 changes: 1 addition & 1 deletion resources/js/fan-chart.min.js

Large diffs are not rendered by default.

21 changes: 13 additions & 8 deletions resources/js/modules/custom/svg/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,12 @@ export default class Text
// The outermost circles show the complete name and do
// not distinguish between first name, last name and dates
if (datum.depth >= 7) {
const nameGroups = this.createNamesData(datum);
const [first, ...last] = this.createNamesData(datum);
const availableWidth = this.getAvailableWidth(datum, 0);

// Merge the firstname and lastname groups, as we display the whole name in one line
const combined = [].concat(first, typeof last[0] !== "undefined" ? last[0] : []);

let text1 = parent
.append("text")
.attr("dy", "2px");
Expand All @@ -129,10 +132,7 @@ export default class Text
text1,
this.truncateNamesData(
text1,
[
...nameGroups[0],
...nameGroups[1],
],
combined,
availableWidth
)
);
Expand Down Expand Up @@ -242,13 +242,16 @@ export default class Text
let lastnames = {};
let minPosFirstnames = Number.MAX_SAFE_INTEGER;
let minPosLastnames = Number.MAX_SAFE_INTEGER;
let offset = 0;

// Iterate over the individual name components and determine their position in the overall
// name and insert the component at the corresponding position in the result object.
for (let i in datum.data.data.firstNames) {
const pos = datum.data.data.name.indexOf(datum.data.data.firstNames[i]);
const pos = datum.data.data.name.indexOf(datum.data.data.firstNames[i], offset);

if (pos !== -1) {
offset = pos;

if (pos < minPosFirstnames) {
minPosFirstnames = pos;
}
Expand All @@ -265,9 +268,11 @@ export default class Text
names[minPosFirstnames] = Object.values(firstnames);

for (let i in datum.data.data.lastNames) {
const pos = datum.data.data.name.indexOf(datum.data.data.lastNames[i]);
const pos = datum.data.data.name.indexOf(datum.data.data.lastNames[i], offset);

if (pos !== -1) {
offset = pos;

if (pos < minPosLastnames) {
minPosLastnames = pos;
}
Expand All @@ -283,7 +288,7 @@ export default class Text

names[minPosLastnames] = Object.values(lastnames);

// Extract the values (keys doesn't matter anymore)
// Extract the values (keys don't matter anymore)
return Object.values(names);
}

Expand Down
82 changes: 65 additions & 17 deletions src/Processor/NameProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,25 @@ class NameProcessor
*/
private const FULL_NAME = 'full';

private const XPATH_FIRST_NAMES_ALL
= './/text()';

private const XPATH_FIRST_NAMES_EXCEPT_PART
= '(//q[@class="wt-nickname"]/text() | //span[@class="SURN"]/text() | //span[@class="SURN"]/following::text())';

/**
* The XPath identifier to extract the first name parts (including the prefix).
*
* As PHP does not support XPath 2.0 "except" => XPATH_FIRST_NAMES_ALL except XPATH_FIRST_NAMES_EXCEPT_PART
*/
private const XPATH_FIRST_NAMES
= self::XPATH_FIRST_NAMES_ALL . '[count(.|' . self::XPATH_FIRST_NAMES_EXCEPT_PART . ')!=count(' . self::XPATH_FIRST_NAMES_EXCEPT_PART . ')]';

/**
* The XPath identifier to extract the last name parts (surname + surname suffix).
*/
private const XPATH_LAST_NAMES = '//span[@class="NAME"]//span[@class="SURN"]/text()|//span[@class="SURN"]/following::text()';

/**
* The XPath identifier to extract the starred name part.
*/
Expand Down Expand Up @@ -97,7 +116,7 @@ private function getDomXPathInstance(string $input): DOMXPath
/**
* Extracts the primary name from the individual.
*
* @param Individual|null $spouse
* @param null|Individual $spouse
* @param bool $useMarriedName TRUE to return the married name instead of the primary one
*
* @return array<string, string>
Expand Down Expand Up @@ -164,36 +183,65 @@ private function replacePlaceholders(string $value): string
);
}

/**
* Returns the full name of the individual without formatting of the individual parts of the name.
* All placeholders were removed as we do not need them in this module.
*
* @return string
*/
public function getFullName(): string
{
// The name of the person without formatting of the individual parts of the name.
// Remove placeholders as we do not need them in this module
return $this->replacePlaceholders($this->primaryName[self::FULL_NAME_WITH_PLACEHOLDERS]);
}

/**
* Splits a name into an array, removing all name placeholders.
*
* @param string $name
* @param string[] $names
*
* @return string[]
*/
private function splitAndCleanName(string $name): array
private function splitAndCleanName(array $names): array
{
$values = [[]];

foreach ($names as $name) {
$values[] = explode(' ', $name);
}

// Remove empty values and reindex array
return array_values(
array_filter(
explode(
' ',
$this->replacePlaceholders($name)
)
array_merge(...$values)
)
);
}

/**
* Returns the full name of the individual without formatting of the individual parts of the name.
* All placeholders were removed as we do not need them in this module.
* Returns all name parts by given identifier.
*
* @return string
* @param string $expression The XPath expression to execute
*
* @return string[]
*/
public function getFullName(): string
private function getNamesByIdentifier(string $expression): array
{
// The name of the person without formatting of the individual parts of the name.
// Remove placeholders as we do not need them in this module
return $this->replacePlaceholders($this->primaryName[self::FULL_NAME_WITH_PLACEHOLDERS]);
$nodeList = $this->xPath->query($expression);
$names = [];

if ($nodeList !== false) {
/** @var DOMNode $node */
foreach ($nodeList as $node) {

Check failure on line 236 in src/Processor/NameProcessor.php

View workflow job for this annotation

GitHub Actions / build (7.4)

PHPDoc tag @var for variable $node contains unknown class MagicSunday\Webtrees\FanChart\Processor\DOMNode.

Check failure on line 236 in src/Processor/NameProcessor.php

View workflow job for this annotation

GitHub Actions / build (8.0)

PHPDoc tag @var for variable $node contains unknown class MagicSunday\Webtrees\FanChart\Processor\DOMNode.

Check failure on line 236 in src/Processor/NameProcessor.php

View workflow job for this annotation

GitHub Actions / build (8.1)

PHPDoc tag @var for variable $node contains unknown class MagicSunday\Webtrees\FanChart\Processor\DOMNode.

Check failure on line 236 in src/Processor/NameProcessor.php

View workflow job for this annotation

GitHub Actions / build (8.2)

PHPDoc tag @var for variable $node contains unknown class MagicSunday\Webtrees\FanChart\Processor\DOMNode.
$names[] = $node->nodeValue ?? '';

Check failure on line 237 in src/Processor/NameProcessor.php

View workflow job for this annotation

GitHub Actions / build (7.4)

Access to property $nodeValue on an unknown class MagicSunday\Webtrees\FanChart\Processor\DOMNode.

Check failure on line 237 in src/Processor/NameProcessor.php

View workflow job for this annotation

GitHub Actions / build (8.0)

Access to property $nodeValue on an unknown class MagicSunday\Webtrees\FanChart\Processor\DOMNode.

Check failure on line 237 in src/Processor/NameProcessor.php

View workflow job for this annotation

GitHub Actions / build (8.1)

Access to property $nodeValue on an unknown class MagicSunday\Webtrees\FanChart\Processor\DOMNode.

Check failure on line 237 in src/Processor/NameProcessor.php

View workflow job for this annotation

GitHub Actions / build (8.2)

Access to property $nodeValue on an unknown class MagicSunday\Webtrees\FanChart\Processor\DOMNode.
}
}

// Remove all leading/trailing whitespace characters
$names = array_map('trim', $names);

return $this->splitAndCleanName($names);
}

/**
Expand All @@ -203,7 +251,7 @@ public function getFullName(): string
*/
public function getFirstNames(): array
{
return $this->splitAndCleanName($this->primaryName['givn']);
return $this->getNamesByIdentifier(self::XPATH_FIRST_NAMES);
}

/**
Expand All @@ -213,7 +261,7 @@ public function getFirstNames(): array
*/
public function getLastNames(): array
{
return $this->splitAndCleanName($this->primaryName['surn']);
return $this->getNamesByIdentifier(self::XPATH_LAST_NAMES);
}

/**
Expand Down Expand Up @@ -247,7 +295,7 @@ public function getAlternateName(Individual $individual): string
$individual->canShowName()
&& ($individual->getPrimaryName() !== $individual->getSecondaryName())
) {
$allNames = $individual->getAllNames();
$allNames = $individual->getAllNames();
$alternativeName = $allNames[$individual->getSecondaryName()][self::FULL_NAME_WITH_PLACEHOLDERS];

return $this->replacePlaceholders($alternativeName);
Expand Down

0 comments on commit 4e57434

Please sign in to comment.