18add1155SRico Sonntag<?php 23976b470SGreg Roach 38add1155SRico Sonntag/** 48add1155SRico Sonntag * webtrees: online genealogy 5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team 68add1155SRico Sonntag * This program is free software: you can redistribute it and/or modify 78add1155SRico Sonntag * it under the terms of the GNU General Public License as published by 88add1155SRico Sonntag * the Free Software Foundation, either version 3 of the License, or 98add1155SRico Sonntag * (at your option) any later version. 108add1155SRico Sonntag * This program is distributed in the hope that it will be useful, 118add1155SRico Sonntag * but WITHOUT ANY WARRANTY; without even the implied warranty of 128add1155SRico Sonntag * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 138add1155SRico Sonntag * GNU General Public License for more details. 148add1155SRico Sonntag * You should have received a copy of the GNU General Public License 1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>. 168add1155SRico Sonntag */ 17fcfa147eSGreg Roach 188add1155SRico Sonntagdeclare(strict_types=1); 198add1155SRico Sonntag 208add1155SRico Sonntagnamespace Fisharebest\Webtrees\Statistics\Repository; 218add1155SRico Sonntag 228add1155SRico Sonntaguse Fisharebest\Webtrees\Auth; 238add1155SRico Sonntaguse Fisharebest\Webtrees\Gedcom; 24d1a467e4SGreg Roachuse Fisharebest\Webtrees\GedcomRecord; 258add1155SRico Sonntaguse Fisharebest\Webtrees\I18N; 268add1155SRico Sonntaguse Fisharebest\Webtrees\Individual; 2767992b6aSRichard Cisseeuse Fisharebest\Webtrees\Module\IndividualListModule; 2867992b6aSRichard Cisseeuse Fisharebest\Webtrees\Module\ModuleInterface; 2987cca37cSGreg Roachuse Fisharebest\Webtrees\Module\ModuleListInterface; 30f78da678SGreg Roachuse Fisharebest\Webtrees\Registry; 3167992b6aSRichard Cisseeuse Fisharebest\Webtrees\Services\ModuleService; 328add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartAge; 338add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartBirth; 348add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartCommonGiven; 358add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartCommonSurname; 368add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartDeath; 378add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartFamilyWithSources; 3888de55fdSRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartIndividualWithSources; 398add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartMortality; 408add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartSex; 418add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Repository\Interfaces\IndividualRepositoryInterface; 42f78da678SGreg Roachuse Fisharebest\Webtrees\Statistics\Service\CenturyService; 43f78da678SGreg Roachuse Fisharebest\Webtrees\Statistics\Service\ColorService; 448add1155SRico Sonntaguse Fisharebest\Webtrees\Tree; 458add1155SRico Sonntaguse Illuminate\Database\Capsule\Manager as DB; 463dc8167dSGreg Roachuse Illuminate\Database\Query\Builder; 47a69f5655SGreg Roachuse Illuminate\Database\Query\Expression; 488add1155SRico Sonntaguse Illuminate\Database\Query\JoinClause; 4976d39c55SGreg Roachuse stdClass; 5076d39c55SGreg Roach 514c78e066SGreg Roachuse function app; 5271378461SGreg Roachuse function array_key_exists; 531c6e5e0cSGreg Roachuse function array_keys; 544c78e066SGreg Roachuse function array_reverse; 551c6e5e0cSGreg Roachuse function array_shift; 5671378461SGreg Roachuse function array_slice; 574c78e066SGreg Roachuse function array_walk; 584c78e066SGreg Roachuse function arsort; 5910e06497SGreg Roachuse function assert; 604c78e066SGreg Roachuse function e; 614c78e066SGreg Roachuse function explode; 621c6e5e0cSGreg Roachuse function implode; 634c78e066SGreg Roachuse function preg_match; 644c78e066SGreg Roachuse function uksort; 654c78e066SGreg Roachuse function view; 6671378461SGreg Roach 678add1155SRico Sonntag/** 684c78e066SGreg Roach * A repository providing methods for individual related statistics. 698add1155SRico Sonntag */ 708add1155SRico Sonntagclass IndividualRepository implements IndividualRepositoryInterface 718add1155SRico Sonntag{ 72f78da678SGreg Roach private CenturyService $century_service; 73f78da678SGreg Roach 74f78da678SGreg Roach private ColorService $color_service; 75f78da678SGreg Roach 764c78e066SGreg Roach private Tree $tree; 778add1155SRico Sonntag 788add1155SRico Sonntag /** 79f78da678SGreg Roach * @param CenturyService $century_service 80f78da678SGreg Roach * @param ColorService $color_service 818add1155SRico Sonntag * @param Tree $tree 828add1155SRico Sonntag */ 83f78da678SGreg Roach public function __construct(CenturyService $century_service, ColorService $color_service, Tree $tree) 848add1155SRico Sonntag { 85f78da678SGreg Roach $this->century_service = $century_service; 86f78da678SGreg Roach $this->color_service = $color_service; 878add1155SRico Sonntag $this->tree = $tree; 888add1155SRico Sonntag } 898add1155SRico Sonntag 908add1155SRico Sonntag /** 918add1155SRico Sonntag * Find common given names. 928add1155SRico Sonntag * 938add1155SRico Sonntag * @param string $sex 948add1155SRico Sonntag * @param string $type 958add1155SRico Sonntag * @param bool $show_tot 968add1155SRico Sonntag * @param int $threshold 978add1155SRico Sonntag * @param int $maxtoshow 988add1155SRico Sonntag * 9909482a55SGreg Roach * @return string|array<int> 1008add1155SRico Sonntag */ 1018add1155SRico Sonntag private function commonGivenQuery(string $sex, string $type, bool $show_tot, int $threshold, int $maxtoshow) 1028add1155SRico Sonntag { 103d1a467e4SGreg Roach $query = DB::table('name') 1040b5fd0a6SGreg Roach ->join('individuals', static function (JoinClause $join): void { 105d1a467e4SGreg Roach $join 106d1a467e4SGreg Roach ->on('i_file', '=', 'n_file') 107d1a467e4SGreg Roach ->on('i_id', '=', 'n_id'); 108d1a467e4SGreg Roach }) 109d1a467e4SGreg Roach ->where('n_file', '=', $this->tree->id()) 110d1a467e4SGreg Roach ->where('n_type', '<>', '_MARNM') 1118fb4e87cSGreg Roach ->where('n_givn', '<>', Individual::PRAENOMEN_NESCIO) 112a69f5655SGreg Roach ->where(new Expression('LENGTH(n_givn)'), '>', 1); 113d1a467e4SGreg Roach 1148add1155SRico Sonntag switch ($sex) { 1158add1155SRico Sonntag case 'M': 1168add1155SRico Sonntag case 'F': 1178add1155SRico Sonntag case 'U': 118d1a467e4SGreg Roach $query->where('i_sex', '=', $sex); 1198add1155SRico Sonntag break; 120d1a467e4SGreg Roach 1218add1155SRico Sonntag case 'B': 1228add1155SRico Sonntag default: 123d1a467e4SGreg Roach $query->where('i_sex', '<>', 'U'); 1248add1155SRico Sonntag break; 1258add1155SRico Sonntag } 1268add1155SRico Sonntag 127d1a467e4SGreg Roach $rows = $query 1283413ec75SRico Sonntag ->groupBy(['n_givn']) 129a69f5655SGreg Roach ->select(['n_givn', new Expression('COUNT(distinct n_id) AS count')]) 130d1a467e4SGreg Roach ->pluck('count', 'n_givn'); 1318add1155SRico Sonntag 1328add1155SRico Sonntag $nameList = []; 1338add1155SRico Sonntag 134d1a467e4SGreg Roach foreach ($rows as $n_givn => $count) { 1358add1155SRico Sonntag // Split “John Thomas” into “John” and “Thomas” and count against both totals 13621f5b67aSGreg Roach foreach (explode(' ', (string) $n_givn) as $given) { 1378add1155SRico Sonntag // Exclude initials and particles. 138ef475b14SGreg Roach if (preg_match('/^([A-Z]|[a-z]{1,3})$/', $given) !== 1) { 1396ccdf4f0SGreg Roach if (array_key_exists($given, $nameList)) { 140d1a467e4SGreg Roach $nameList[$given] += (int) $count; 1418add1155SRico Sonntag } else { 142d1a467e4SGreg Roach $nameList[$given] = (int) $count; 1438add1155SRico Sonntag } 1448add1155SRico Sonntag } 1458add1155SRico Sonntag } 1468add1155SRico Sonntag } 1478add1155SRico Sonntag arsort($nameList); 1486ccdf4f0SGreg Roach $nameList = array_slice($nameList, 0, $maxtoshow); 1498add1155SRico Sonntag 1508add1155SRico Sonntag foreach ($nameList as $given => $total) { 1518add1155SRico Sonntag if ($total < $threshold) { 1528add1155SRico Sonntag unset($nameList[$given]); 1538add1155SRico Sonntag } 1548add1155SRico Sonntag } 1558add1155SRico Sonntag 1568add1155SRico Sonntag switch ($type) { 1578add1155SRico Sonntag case 'chart': 1588add1155SRico Sonntag return $nameList; 1598add1155SRico Sonntag 1608add1155SRico Sonntag case 'table': 1618add1155SRico Sonntag return view('lists/given-names-table', [ 1628add1155SRico Sonntag 'given_names' => $nameList, 163c9128110SGreg Roach 'order' => [[1, 'desc']], 1648add1155SRico Sonntag ]); 1658add1155SRico Sonntag 1668add1155SRico Sonntag case 'list': 1678add1155SRico Sonntag return view('lists/given-names-list', [ 1688add1155SRico Sonntag 'given_names' => $nameList, 1698add1155SRico Sonntag 'show_totals' => $show_tot, 1708add1155SRico Sonntag ]); 1718add1155SRico Sonntag 1728add1155SRico Sonntag case 'nolist': 1738add1155SRico Sonntag default: 1740b5fd0a6SGreg Roach array_walk($nameList, static function (string &$value, string $key) use ($show_tot): void { 1758add1155SRico Sonntag if ($show_tot) { 176315eb316SGreg Roach $value = '<bdi>' . e($key) . '</bdi> (' . I18N::number((int) $value) . ')'; 1772da2e0a6SGreg Roach } else { 178315eb316SGreg Roach $value = '<bdi>' . e($key) . '</bdi>'; 1798add1155SRico Sonntag } 1808add1155SRico Sonntag }); 1818add1155SRico Sonntag 1828add1155SRico Sonntag return implode(I18N::$list_separator, $nameList); 1838add1155SRico Sonntag } 1848add1155SRico Sonntag } 1858add1155SRico Sonntag 1868add1155SRico Sonntag /** 1878add1155SRico Sonntag * Find common give names. 1888add1155SRico Sonntag * 1898add1155SRico Sonntag * @param int $threshold 1908add1155SRico Sonntag * @param int $maxtoshow 1918add1155SRico Sonntag * 1928add1155SRico Sonntag * @return string 1938add1155SRico Sonntag */ 1948add1155SRico Sonntag public function commonGiven(int $threshold = 1, int $maxtoshow = 10): string 1958add1155SRico Sonntag { 1968add1155SRico Sonntag return $this->commonGivenQuery('B', 'nolist', false, $threshold, $maxtoshow); 1978add1155SRico Sonntag } 1988add1155SRico Sonntag 1998add1155SRico Sonntag /** 2008add1155SRico Sonntag * Find common give names. 2018add1155SRico Sonntag * 2028add1155SRico Sonntag * @param int $threshold 2038add1155SRico Sonntag * @param int $maxtoshow 2048add1155SRico Sonntag * 2058add1155SRico Sonntag * @return string 2068add1155SRico Sonntag */ 2078add1155SRico Sonntag public function commonGivenTotals(int $threshold = 1, int $maxtoshow = 10): string 2088add1155SRico Sonntag { 2098add1155SRico Sonntag return $this->commonGivenQuery('B', 'nolist', true, $threshold, $maxtoshow); 2108add1155SRico Sonntag } 2118add1155SRico Sonntag 2128add1155SRico Sonntag /** 2138add1155SRico Sonntag * Find common give names. 2148add1155SRico Sonntag * 2158add1155SRico Sonntag * @param int $threshold 2168add1155SRico Sonntag * @param int $maxtoshow 2178add1155SRico Sonntag * 2188add1155SRico Sonntag * @return string 2198add1155SRico Sonntag */ 2208add1155SRico Sonntag public function commonGivenList(int $threshold = 1, int $maxtoshow = 10): string 2218add1155SRico Sonntag { 2228add1155SRico Sonntag return $this->commonGivenQuery('B', 'list', false, $threshold, $maxtoshow); 2238add1155SRico Sonntag } 2248add1155SRico Sonntag 2258add1155SRico Sonntag /** 2268add1155SRico Sonntag * Find common give names. 2278add1155SRico Sonntag * 2288add1155SRico Sonntag * @param int $threshold 2298add1155SRico Sonntag * @param int $maxtoshow 2308add1155SRico Sonntag * 2318add1155SRico Sonntag * @return string 2328add1155SRico Sonntag */ 2338add1155SRico Sonntag public function commonGivenListTotals(int $threshold = 1, int $maxtoshow = 10): string 2348add1155SRico Sonntag { 2358add1155SRico Sonntag return $this->commonGivenQuery('B', 'list', true, $threshold, $maxtoshow); 2368add1155SRico Sonntag } 2378add1155SRico Sonntag 2388add1155SRico Sonntag /** 2398add1155SRico Sonntag * Find common give names. 2408add1155SRico Sonntag * 2418add1155SRico Sonntag * @param int $threshold 2428add1155SRico Sonntag * @param int $maxtoshow 2438add1155SRico Sonntag * 2448add1155SRico Sonntag * @return string 2458add1155SRico Sonntag */ 2468add1155SRico Sonntag public function commonGivenTable(int $threshold = 1, int $maxtoshow = 10): string 2478add1155SRico Sonntag { 2488add1155SRico Sonntag return $this->commonGivenQuery('B', 'table', false, $threshold, $maxtoshow); 2498add1155SRico Sonntag } 2508add1155SRico Sonntag 2518add1155SRico Sonntag /** 2528add1155SRico Sonntag * Find common give names of females. 2538add1155SRico Sonntag * 2548add1155SRico Sonntag * @param int $threshold 2558add1155SRico Sonntag * @param int $maxtoshow 2568add1155SRico Sonntag * 2578add1155SRico Sonntag * @return string 2588add1155SRico Sonntag */ 2598add1155SRico Sonntag public function commonGivenFemale(int $threshold = 1, int $maxtoshow = 10): string 2608add1155SRico Sonntag { 2618add1155SRico Sonntag return $this->commonGivenQuery('F', 'nolist', false, $threshold, $maxtoshow); 2628add1155SRico Sonntag } 2638add1155SRico Sonntag 2648add1155SRico Sonntag /** 2658add1155SRico Sonntag * Find common give names of females. 2668add1155SRico Sonntag * 2678add1155SRico Sonntag * @param int $threshold 2688add1155SRico Sonntag * @param int $maxtoshow 2698add1155SRico Sonntag * 2708add1155SRico Sonntag * @return string 2718add1155SRico Sonntag */ 2728add1155SRico Sonntag public function commonGivenFemaleTotals(int $threshold = 1, int $maxtoshow = 10): string 2738add1155SRico Sonntag { 2748add1155SRico Sonntag return $this->commonGivenQuery('F', 'nolist', true, $threshold, $maxtoshow); 2758add1155SRico Sonntag } 2768add1155SRico Sonntag 2778add1155SRico Sonntag /** 2788add1155SRico Sonntag * Find common give names of females. 2798add1155SRico Sonntag * 2808add1155SRico Sonntag * @param int $threshold 2818add1155SRico Sonntag * @param int $maxtoshow 2828add1155SRico Sonntag * 2838add1155SRico Sonntag * @return string 2848add1155SRico Sonntag */ 2858add1155SRico Sonntag public function commonGivenFemaleList(int $threshold = 1, int $maxtoshow = 10): string 2868add1155SRico Sonntag { 2878add1155SRico Sonntag return $this->commonGivenQuery('F', 'list', false, $threshold, $maxtoshow); 2888add1155SRico Sonntag } 2898add1155SRico Sonntag 2908add1155SRico Sonntag /** 2918add1155SRico Sonntag * Find common give names of females. 2928add1155SRico Sonntag * 2938add1155SRico Sonntag * @param int $threshold 2948add1155SRico Sonntag * @param int $maxtoshow 2958add1155SRico Sonntag * 2968add1155SRico Sonntag * @return string 2978add1155SRico Sonntag */ 2988add1155SRico Sonntag public function commonGivenFemaleListTotals(int $threshold = 1, int $maxtoshow = 10): string 2998add1155SRico Sonntag { 3008add1155SRico Sonntag return $this->commonGivenQuery('F', 'list', true, $threshold, $maxtoshow); 3018add1155SRico Sonntag } 3028add1155SRico Sonntag 3038add1155SRico Sonntag /** 3048add1155SRico Sonntag * Find common give names of females. 3058add1155SRico Sonntag * 3068add1155SRico Sonntag * @param int $threshold 3078add1155SRico Sonntag * @param int $maxtoshow 3088add1155SRico Sonntag * 3098add1155SRico Sonntag * @return string 3108add1155SRico Sonntag */ 3118add1155SRico Sonntag public function commonGivenFemaleTable(int $threshold = 1, int $maxtoshow = 10): string 3128add1155SRico Sonntag { 3138add1155SRico Sonntag return $this->commonGivenQuery('F', 'table', false, $threshold, $maxtoshow); 3148add1155SRico Sonntag } 3158add1155SRico Sonntag 3168add1155SRico Sonntag /** 3178add1155SRico Sonntag * Find common give names of males. 3188add1155SRico Sonntag * 3198add1155SRico Sonntag * @param int $threshold 3208add1155SRico Sonntag * @param int $maxtoshow 3218add1155SRico Sonntag * 3228add1155SRico Sonntag * @return string 3238add1155SRico Sonntag */ 3248add1155SRico Sonntag public function commonGivenMale(int $threshold = 1, int $maxtoshow = 10): string 3258add1155SRico Sonntag { 3268add1155SRico Sonntag return $this->commonGivenQuery('M', 'nolist', false, $threshold, $maxtoshow); 3278add1155SRico Sonntag } 3288add1155SRico Sonntag 3298add1155SRico Sonntag /** 3308add1155SRico Sonntag * Find common give names of males. 3318add1155SRico Sonntag * 3328add1155SRico Sonntag * @param int $threshold 3338add1155SRico Sonntag * @param int $maxtoshow 3348add1155SRico Sonntag * 3358add1155SRico Sonntag * @return string 3368add1155SRico Sonntag */ 3378add1155SRico Sonntag public function commonGivenMaleTotals(int $threshold = 1, int $maxtoshow = 10): string 3388add1155SRico Sonntag { 3398add1155SRico Sonntag return $this->commonGivenQuery('M', 'nolist', true, $threshold, $maxtoshow); 3408add1155SRico Sonntag } 3418add1155SRico Sonntag 3428add1155SRico Sonntag /** 3438add1155SRico Sonntag * Find common give names of males. 3448add1155SRico Sonntag * 3458add1155SRico Sonntag * @param int $threshold 3468add1155SRico Sonntag * @param int $maxtoshow 3478add1155SRico Sonntag * 3488add1155SRico Sonntag * @return string 3498add1155SRico Sonntag */ 3508add1155SRico Sonntag public function commonGivenMaleList(int $threshold = 1, int $maxtoshow = 10): string 3518add1155SRico Sonntag { 3528add1155SRico Sonntag return $this->commonGivenQuery('M', 'list', false, $threshold, $maxtoshow); 3538add1155SRico Sonntag } 3548add1155SRico Sonntag 3558add1155SRico Sonntag /** 3568add1155SRico Sonntag * Find common give names of males. 3578add1155SRico Sonntag * 3588add1155SRico Sonntag * @param int $threshold 3598add1155SRico Sonntag * @param int $maxtoshow 3608add1155SRico Sonntag * 3618add1155SRico Sonntag * @return string 3628add1155SRico Sonntag */ 3638add1155SRico Sonntag public function commonGivenMaleListTotals(int $threshold = 1, int $maxtoshow = 10): string 3648add1155SRico Sonntag { 3658add1155SRico Sonntag return $this->commonGivenQuery('M', 'list', true, $threshold, $maxtoshow); 3668add1155SRico Sonntag } 3678add1155SRico Sonntag 3688add1155SRico Sonntag /** 3698add1155SRico Sonntag * Find common give names of males. 3708add1155SRico Sonntag * 3718add1155SRico Sonntag * @param int $threshold 3728add1155SRico Sonntag * @param int $maxtoshow 3738add1155SRico Sonntag * 3748add1155SRico Sonntag * @return string 3758add1155SRico Sonntag */ 3768add1155SRico Sonntag public function commonGivenMaleTable(int $threshold = 1, int $maxtoshow = 10): string 3778add1155SRico Sonntag { 3788add1155SRico Sonntag return $this->commonGivenQuery('M', 'table', false, $threshold, $maxtoshow); 3798add1155SRico Sonntag } 3808add1155SRico Sonntag 3818add1155SRico Sonntag /** 3828add1155SRico Sonntag * Find common give names of unknown sexes. 3838add1155SRico Sonntag * 3848add1155SRico Sonntag * @param int $threshold 3858add1155SRico Sonntag * @param int $maxtoshow 3868add1155SRico Sonntag * 3878add1155SRico Sonntag * @return string 3888add1155SRico Sonntag */ 3898add1155SRico Sonntag public function commonGivenUnknown(int $threshold = 1, int $maxtoshow = 10): string 3908add1155SRico Sonntag { 3918add1155SRico Sonntag return $this->commonGivenQuery('U', 'nolist', false, $threshold, $maxtoshow); 3928add1155SRico Sonntag } 3938add1155SRico Sonntag 3948add1155SRico Sonntag /** 3958add1155SRico Sonntag * Find common give names of unknown sexes. 3968add1155SRico Sonntag * 3978add1155SRico Sonntag * @param int $threshold 3988add1155SRico Sonntag * @param int $maxtoshow 3998add1155SRico Sonntag * 4008add1155SRico Sonntag * @return string 4018add1155SRico Sonntag */ 4028add1155SRico Sonntag public function commonGivenUnknownTotals(int $threshold = 1, int $maxtoshow = 10): string 4038add1155SRico Sonntag { 4048add1155SRico Sonntag return $this->commonGivenQuery('U', 'nolist', true, $threshold, $maxtoshow); 4058add1155SRico Sonntag } 4068add1155SRico Sonntag 4078add1155SRico Sonntag /** 4088add1155SRico Sonntag * Find common give names of unknown sexes. 4098add1155SRico Sonntag * 4108add1155SRico Sonntag * @param int $threshold 4118add1155SRico Sonntag * @param int $maxtoshow 4128add1155SRico Sonntag * 4138add1155SRico Sonntag * @return string 4148add1155SRico Sonntag */ 4158add1155SRico Sonntag public function commonGivenUnknownList(int $threshold = 1, int $maxtoshow = 10): string 4168add1155SRico Sonntag { 4178add1155SRico Sonntag return $this->commonGivenQuery('U', 'list', false, $threshold, $maxtoshow); 4188add1155SRico Sonntag } 4198add1155SRico Sonntag 4208add1155SRico Sonntag /** 4218add1155SRico Sonntag * Find common give names of unknown sexes. 4228add1155SRico Sonntag * 4238add1155SRico Sonntag * @param int $threshold 4248add1155SRico Sonntag * @param int $maxtoshow 4258add1155SRico Sonntag * 4268add1155SRico Sonntag * @return string 4278add1155SRico Sonntag */ 4288add1155SRico Sonntag public function commonGivenUnknownListTotals(int $threshold = 1, int $maxtoshow = 10): string 4298add1155SRico Sonntag { 4308add1155SRico Sonntag return $this->commonGivenQuery('U', 'list', true, $threshold, $maxtoshow); 4318add1155SRico Sonntag } 4328add1155SRico Sonntag 4338add1155SRico Sonntag /** 4348add1155SRico Sonntag * Find common give names of unknown sexes. 4358add1155SRico Sonntag * 4368add1155SRico Sonntag * @param int $threshold 4378add1155SRico Sonntag * @param int $maxtoshow 4388add1155SRico Sonntag * 4398add1155SRico Sonntag * @return string 4408add1155SRico Sonntag */ 4418add1155SRico Sonntag public function commonGivenUnknownTable(int $threshold = 1, int $maxtoshow = 10): string 4428add1155SRico Sonntag { 4438add1155SRico Sonntag return $this->commonGivenQuery('U', 'table', false, $threshold, $maxtoshow); 4448add1155SRico Sonntag } 4458add1155SRico Sonntag 4468add1155SRico Sonntag /** 4473dc8167dSGreg Roach * Count the number of distinct given names (or the number of occurences of specific given names). 4488add1155SRico Sonntag * 44909482a55SGreg Roach * @param array<string> ...$params 4508add1155SRico Sonntag * 4518add1155SRico Sonntag * @return string 4528add1155SRico Sonntag */ 4538add1155SRico Sonntag public function totalGivennames(...$params): string 4548add1155SRico Sonntag { 4553dc8167dSGreg Roach $query = DB::table('name') 4563dc8167dSGreg Roach ->where('n_file', '=', $this->tree->id()); 4573dc8167dSGreg Roach 458320f6a24SGreg Roach if ($params === []) { 459c09e99bfSRico Sonntag // Count number of distinct given names. 4603dc8167dSGreg Roach $query 461c09e99bfSRico Sonntag ->distinct() 4628fb4e87cSGreg Roach ->where('n_givn', '<>', Individual::PRAENOMEN_NESCIO) 463c09e99bfSRico Sonntag ->whereNotNull('n_givn'); 4648add1155SRico Sonntag } else { 465c09e99bfSRico Sonntag // Count number of occurences of specific given names. 46644cdc21eSGreg Roach $query->whereIn('n_givn', $params); 4678add1155SRico Sonntag } 4688add1155SRico Sonntag 469c09e99bfSRico Sonntag $count = $query->count('n_givn'); 4703dc8167dSGreg Roach 4713dc8167dSGreg Roach return I18N::number($count); 4728add1155SRico Sonntag } 4738add1155SRico Sonntag 4748add1155SRico Sonntag /** 475320f6a24SGreg Roach * Count the number of distinct surnames (or the number of occurrences of specific surnames). 4768add1155SRico Sonntag * 47709482a55SGreg Roach * @param array<string> ...$params 4788add1155SRico Sonntag * 4798add1155SRico Sonntag * @return string 4808add1155SRico Sonntag */ 4818add1155SRico Sonntag public function totalSurnames(...$params): string 4828add1155SRico Sonntag { 4833dc8167dSGreg Roach $query = DB::table('name') 4843dc8167dSGreg Roach ->where('n_file', '=', $this->tree->id()); 4853dc8167dSGreg Roach 486320f6a24SGreg Roach if ($params === []) { 4873dc8167dSGreg Roach // Count number of distinct surnames 488c09e99bfSRico Sonntag $query->distinct() 489c09e99bfSRico Sonntag ->whereNotNull('n_surn'); 4908add1155SRico Sonntag } else { 4913dc8167dSGreg Roach // Count number of occurences of specific surnames. 4923dc8167dSGreg Roach $query->whereIn('n_surn', $params); 4938add1155SRico Sonntag } 4948add1155SRico Sonntag 495c09e99bfSRico Sonntag $count = $query->count('n_surn'); 4968add1155SRico Sonntag 4973dc8167dSGreg Roach return I18N::number($count); 4988add1155SRico Sonntag } 4998add1155SRico Sonntag 5008add1155SRico Sonntag /** 5018add1155SRico Sonntag * @param int $number_of_surnames 5028add1155SRico Sonntag * @param int $threshold 5038add1155SRico Sonntag * 50409482a55SGreg Roach * @return array<array<int>> 5058add1155SRico Sonntag */ 5068add1155SRico Sonntag private function topSurnames(int $number_of_surnames, int $threshold): array 5078add1155SRico Sonntag { 5088add1155SRico Sonntag // Use the count of base surnames. 509d1a467e4SGreg Roach $top_surnames = DB::table('name') 510d1a467e4SGreg Roach ->where('n_file', '=', $this->tree->id()) 511d1a467e4SGreg Roach ->where('n_type', '<>', '_MARNM') 5128fb4e87cSGreg Roach ->whereNotIn('n_surn', ['', Individual::NOMEN_NESCIO]) 51347256fc5SGreg Roach ->select(['n_surn']) 5147f5c2944SGreg Roach ->groupBy(['n_surn']) 5152da2e0a6SGreg Roach ->orderByRaw('COUNT(n_surn) DESC') 5162da2e0a6SGreg Roach ->orderBy(new Expression('COUNT(n_surn)'), 'DESC') 5172da2e0a6SGreg Roach ->having(new Expression('COUNT(n_surn)'), '>=', $threshold) 518d1a467e4SGreg Roach ->take($number_of_surnames) 519d1a467e4SGreg Roach ->get() 520d1a467e4SGreg Roach ->pluck('n_surn') 521d1a467e4SGreg Roach ->all(); 5228add1155SRico Sonntag 5238add1155SRico Sonntag $surnames = []; 5242da2e0a6SGreg Roach 5258add1155SRico Sonntag foreach ($top_surnames as $top_surname) { 5262da2e0a6SGreg Roach $surnames[$top_surname] = DB::table('name') 527d1a467e4SGreg Roach ->where('n_file', '=', $this->tree->id()) 5282da2e0a6SGreg Roach ->where('n_type', '<>', '_MARNM') 5292da2e0a6SGreg Roach ->where('n_surn', '=', $top_surname) 5302da2e0a6SGreg Roach ->select(['n_surn', new Expression('COUNT(n_surn) AS count')]) 5317f5c2944SGreg Roach ->groupBy(['n_surn']) 5322da2e0a6SGreg Roach ->orderBy('n_surn') 533d1a467e4SGreg Roach ->get() 534d1a467e4SGreg Roach ->pluck('count', 'n_surn') 535b7c04225SGreg Roach ->map(static fn (string $count): int => (int) $count) 536d1a467e4SGreg Roach ->all(); 5378add1155SRico Sonntag } 5388add1155SRico Sonntag 5398add1155SRico Sonntag return $surnames; 5408add1155SRico Sonntag } 5418add1155SRico Sonntag 5428add1155SRico Sonntag /** 5438add1155SRico Sonntag * Find common surnames. 5448add1155SRico Sonntag * 5458add1155SRico Sonntag * @return string 5468add1155SRico Sonntag */ 5478add1155SRico Sonntag public function getCommonSurname(): string 5488add1155SRico Sonntag { 5498add1155SRico Sonntag $top_surname = $this->topSurnames(1, 0); 550f24db0ceSRico Sonntag 5511c6e5e0cSGreg Roach return implode(', ', array_keys(array_shift($top_surname) ?? [])); 5528add1155SRico Sonntag } 5538add1155SRico Sonntag 5548add1155SRico Sonntag /** 5558add1155SRico Sonntag * Find common surnames. 5568add1155SRico Sonntag * 5578add1155SRico Sonntag * @param string $type 5588add1155SRico Sonntag * @param bool $show_tot 5598add1155SRico Sonntag * @param int $threshold 5608add1155SRico Sonntag * @param int $number_of_surnames 5618add1155SRico Sonntag * @param string $sorting 5628add1155SRico Sonntag * 5638add1155SRico Sonntag * @return string 5648add1155SRico Sonntag */ 5658add1155SRico Sonntag private function commonSurnamesQuery( 5668add1155SRico Sonntag string $type, 5678add1155SRico Sonntag bool $show_tot, 5688add1155SRico Sonntag int $threshold, 5698add1155SRico Sonntag int $number_of_surnames, 5708add1155SRico Sonntag string $sorting 5718add1155SRico Sonntag ): string { 5728add1155SRico Sonntag $surnames = $this->topSurnames($number_of_surnames, $threshold); 5738add1155SRico Sonntag 5748add1155SRico Sonntag switch ($sorting) { 5758add1155SRico Sonntag default: 5768add1155SRico Sonntag case 'alpha': 57737646143SGreg Roach uksort($surnames, I18N::comparator()); 5788add1155SRico Sonntag break; 5798add1155SRico Sonntag case 'count': 5808add1155SRico Sonntag break; 5818add1155SRico Sonntag case 'rcount': 5828add1155SRico Sonntag $surnames = array_reverse($surnames, true); 5838add1155SRico Sonntag break; 5848add1155SRico Sonntag } 5858add1155SRico Sonntag 58667992b6aSRichard Cissee // find a module providing individual lists 587b55cbc6bSGreg Roach $module_service = app(ModuleService::class); 588b55cbc6bSGreg Roach assert($module_service instanceof ModuleService); 589b55cbc6bSGreg Roach 590b55cbc6bSGreg Roach $module = $module_service 591b55cbc6bSGreg Roach ->findByComponent(ModuleListInterface::class, $this->tree, Auth::user()) 592b55cbc6bSGreg Roach ->first(static fn (ModuleInterface $module): bool => $module instanceof IndividualListModule); 59367992b6aSRichard Cissee 594cd1ec0d0SGreg Roach if ($type === 'list') { 595cd1ec0d0SGreg Roach return view('lists/surnames-bullet-list', [ 596cd1ec0d0SGreg Roach 'surnames' => $surnames, 5976efab796SGreg Roach 'module' => $module, 598cd1ec0d0SGreg Roach 'totals' => $show_tot, 599cd1ec0d0SGreg Roach 'tree' => $this->tree, 600cd1ec0d0SGreg Roach ]); 601cd1ec0d0SGreg Roach } 602cd1ec0d0SGreg Roach 603cd1ec0d0SGreg Roach return view('lists/surnames-compact-list', [ 604cd1ec0d0SGreg Roach 'surnames' => $surnames, 6056efab796SGreg Roach 'module' => $module, 606cd1ec0d0SGreg Roach 'totals' => $show_tot, 607cd1ec0d0SGreg Roach 'tree' => $this->tree, 608cd1ec0d0SGreg Roach ]); 6098add1155SRico Sonntag } 6108add1155SRico Sonntag 6118add1155SRico Sonntag /** 6128add1155SRico Sonntag * Find common surnames. 6138add1155SRico Sonntag * 6148add1155SRico Sonntag * @param int $threshold 6158add1155SRico Sonntag * @param int $number_of_surnames 6168add1155SRico Sonntag * @param string $sorting 6178add1155SRico Sonntag * 6188add1155SRico Sonntag * @return string 6198add1155SRico Sonntag */ 6208add1155SRico Sonntag public function commonSurnames( 6218add1155SRico Sonntag int $threshold = 1, 6228add1155SRico Sonntag int $number_of_surnames = 10, 6238add1155SRico Sonntag string $sorting = 'alpha' 6248add1155SRico Sonntag ): string { 6258add1155SRico Sonntag return $this->commonSurnamesQuery('nolist', false, $threshold, $number_of_surnames, $sorting); 6268add1155SRico Sonntag } 6278add1155SRico Sonntag 6288add1155SRico Sonntag /** 6298add1155SRico Sonntag * Find common surnames. 6308add1155SRico Sonntag * 6318add1155SRico Sonntag * @param int $threshold 6328add1155SRico Sonntag * @param int $number_of_surnames 6338add1155SRico Sonntag * @param string $sorting 6348add1155SRico Sonntag * 6358add1155SRico Sonntag * @return string 6368add1155SRico Sonntag */ 6378add1155SRico Sonntag public function commonSurnamesTotals( 6388add1155SRico Sonntag int $threshold = 1, 6398add1155SRico Sonntag int $number_of_surnames = 10, 6402da2e0a6SGreg Roach string $sorting = 'count' 6418add1155SRico Sonntag ): string { 6428add1155SRico Sonntag return $this->commonSurnamesQuery('nolist', true, $threshold, $number_of_surnames, $sorting); 6438add1155SRico Sonntag } 6448add1155SRico Sonntag 6458add1155SRico Sonntag /** 6468add1155SRico Sonntag * Find common surnames. 6478add1155SRico Sonntag * 6488add1155SRico Sonntag * @param int $threshold 6498add1155SRico Sonntag * @param int $number_of_surnames 6508add1155SRico Sonntag * @param string $sorting 6518add1155SRico Sonntag * 6528add1155SRico Sonntag * @return string 6538add1155SRico Sonntag */ 6548add1155SRico Sonntag public function commonSurnamesList( 6558add1155SRico Sonntag int $threshold = 1, 6568add1155SRico Sonntag int $number_of_surnames = 10, 6578add1155SRico Sonntag string $sorting = 'alpha' 6588add1155SRico Sonntag ): string { 6598add1155SRico Sonntag return $this->commonSurnamesQuery('list', false, $threshold, $number_of_surnames, $sorting); 6608add1155SRico Sonntag } 6618add1155SRico Sonntag 6628add1155SRico Sonntag /** 6638add1155SRico Sonntag * Find common surnames. 6648add1155SRico Sonntag * 6658add1155SRico Sonntag * @param int $threshold 6668add1155SRico Sonntag * @param int $number_of_surnames 6678add1155SRico Sonntag * @param string $sorting 6688add1155SRico Sonntag * 6698add1155SRico Sonntag * @return string 6708add1155SRico Sonntag */ 6718add1155SRico Sonntag public function commonSurnamesListTotals( 6728add1155SRico Sonntag int $threshold = 1, 6738add1155SRico Sonntag int $number_of_surnames = 10, 6742da2e0a6SGreg Roach string $sorting = 'count' 6758add1155SRico Sonntag ): string { 6768add1155SRico Sonntag return $this->commonSurnamesQuery('list', true, $threshold, $number_of_surnames, $sorting); 6778add1155SRico Sonntag } 6788add1155SRico Sonntag 6798add1155SRico Sonntag /** 680cde1d378SGreg Roach * Get a count of births by month. 6818add1155SRico Sonntag * 6828add1155SRico Sonntag * @param int $year1 6838add1155SRico Sonntag * @param int $year2 6848add1155SRico Sonntag * 685cde1d378SGreg Roach * @return Builder 6868add1155SRico Sonntag */ 687cde1d378SGreg Roach public function statsBirthQuery(int $year1 = -1, int $year2 = -1): Builder 6888add1155SRico Sonntag { 689d1a467e4SGreg Roach $query = DB::table('dates') 690a69f5655SGreg Roach ->select(['d_month', new Expression('COUNT(*) AS total')]) 691d1a467e4SGreg Roach ->where('d_file', '=', $this->tree->id()) 692d1a467e4SGreg Roach ->where('d_fact', '=', 'BIRT') 693d1a467e4SGreg Roach ->whereIn('d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 6947f5c2944SGreg Roach ->groupBy(['d_month']); 6958add1155SRico Sonntag 6968add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 697d1a467e4SGreg Roach $query->whereBetween('d_year', [$year1, $year2]); 6988add1155SRico Sonntag } 6998add1155SRico Sonntag 700cde1d378SGreg Roach return $query; 701cde1d378SGreg Roach } 702cde1d378SGreg Roach 703cde1d378SGreg Roach /** 704cde1d378SGreg Roach * Get a count of births by month. 705cde1d378SGreg Roach * 706cde1d378SGreg Roach * @param int $year1 707cde1d378SGreg Roach * @param int $year2 708cde1d378SGreg Roach * 709cde1d378SGreg Roach * @return Builder 710cde1d378SGreg Roach */ 711cde1d378SGreg Roach public function statsBirthBySexQuery(int $year1 = -1, int $year2 = -1): Builder 712cde1d378SGreg Roach { 713cde1d378SGreg Roach return $this->statsBirthQuery($year1, $year2) 714a69f5655SGreg Roach ->select(['d_month', 'i_sex', new Expression('COUNT(*) AS total')]) 7150b5fd0a6SGreg Roach ->join('individuals', static function (JoinClause $join): void { 716d1a467e4SGreg Roach $join 717d1a467e4SGreg Roach ->on('i_id', '=', 'd_gid') 718d1a467e4SGreg Roach ->on('i_file', '=', 'd_file'); 719d1a467e4SGreg Roach }) 7207f5c2944SGreg Roach ->groupBy(['i_sex']); 7218add1155SRico Sonntag } 7228add1155SRico Sonntag 7238add1155SRico Sonntag /** 7248add1155SRico Sonntag * General query on births. 7258add1155SRico Sonntag * 7268add1155SRico Sonntag * @param string|null $color_from 7278add1155SRico Sonntag * @param string|null $color_to 7288add1155SRico Sonntag * 7298add1155SRico Sonntag * @return string 7308add1155SRico Sonntag */ 73188de55fdSRico Sonntag public function statsBirth(string $color_from = null, string $color_to = null): string 7328add1155SRico Sonntag { 733f78da678SGreg Roach return (new ChartBirth($this->century_service, $this->color_service, $this->tree)) 73488de55fdSRico Sonntag ->chartBirth($color_from, $color_to); 7358add1155SRico Sonntag } 7368add1155SRico Sonntag 7378add1155SRico Sonntag /** 7388add1155SRico Sonntag * Get a list of death dates. 7398add1155SRico Sonntag * 7408add1155SRico Sonntag * @param int $year1 7418add1155SRico Sonntag * @param int $year2 7428add1155SRico Sonntag * 743cde1d378SGreg Roach * @return Builder 7448add1155SRico Sonntag */ 745cde1d378SGreg Roach public function statsDeathQuery(int $year1 = -1, int $year2 = -1): Builder 7468add1155SRico Sonntag { 747d1a467e4SGreg Roach $query = DB::table('dates') 748a69f5655SGreg Roach ->select(['d_month', new Expression('COUNT(*) AS total')]) 749d1a467e4SGreg Roach ->where('d_file', '=', $this->tree->id()) 750d1a467e4SGreg Roach ->where('d_fact', '=', 'DEAT') 751d1a467e4SGreg Roach ->whereIn('d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 7527f5c2944SGreg Roach ->groupBy(['d_month']); 7538add1155SRico Sonntag 7548add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 755d1a467e4SGreg Roach $query->whereBetween('d_year', [$year1, $year2]); 7568add1155SRico Sonntag } 7578add1155SRico Sonntag 758cde1d378SGreg Roach return $query; 759cde1d378SGreg Roach } 760cde1d378SGreg Roach 761cde1d378SGreg Roach /** 762cde1d378SGreg Roach * Get a list of death dates. 763cde1d378SGreg Roach * 764cde1d378SGreg Roach * @param int $year1 765cde1d378SGreg Roach * @param int $year2 766cde1d378SGreg Roach * 767cde1d378SGreg Roach * @return Builder 768cde1d378SGreg Roach */ 769cde1d378SGreg Roach public function statsDeathBySexQuery(int $year1 = -1, int $year2 = -1): Builder 770cde1d378SGreg Roach { 771cde1d378SGreg Roach return $this->statsDeathQuery($year1, $year2) 772a69f5655SGreg Roach ->select(['d_month', 'i_sex', new Expression('COUNT(*) AS total')]) 7730b5fd0a6SGreg Roach ->join('individuals', static function (JoinClause $join): void { 774d1a467e4SGreg Roach $join 775d1a467e4SGreg Roach ->on('i_id', '=', 'd_gid') 776d1a467e4SGreg Roach ->on('i_file', '=', 'd_file'); 777d1a467e4SGreg Roach }) 7787f5c2944SGreg Roach ->groupBy(['i_sex']); 7798add1155SRico Sonntag } 7808add1155SRico Sonntag 7818add1155SRico Sonntag /** 7828add1155SRico Sonntag * General query on deaths. 7838add1155SRico Sonntag * 7848add1155SRico Sonntag * @param string|null $color_from 7858add1155SRico Sonntag * @param string|null $color_to 7868add1155SRico Sonntag * 7878add1155SRico Sonntag * @return string 7888add1155SRico Sonntag */ 78988de55fdSRico Sonntag public function statsDeath(string $color_from = null, string $color_to = null): string 7908add1155SRico Sonntag { 791f78da678SGreg Roach return (new ChartDeath($this->century_service, $this->color_service, $this->tree)) 79288de55fdSRico Sonntag ->chartDeath($color_from, $color_to); 7938add1155SRico Sonntag } 7948add1155SRico Sonntag 7958add1155SRico Sonntag /** 7968add1155SRico Sonntag * General query on ages. 7978add1155SRico Sonntag * 7988add1155SRico Sonntag * @param string $related 7998add1155SRico Sonntag * @param string $sex 8008add1155SRico Sonntag * @param int $year1 8018add1155SRico Sonntag * @param int $year2 8028add1155SRico Sonntag * 80376d39c55SGreg Roach * @return array<stdClass> 8048add1155SRico Sonntag */ 805d823340dSGreg Roach public function statsAgeQuery(string $related = 'BIRT', string $sex = 'BOTH', int $year1 = -1, int $year2 = -1): array 8068add1155SRico Sonntag { 80744cdc21eSGreg Roach $prefix = DB::connection()->getTablePrefix(); 8088add1155SRico Sonntag 80944cdc21eSGreg Roach $query = $this->birthAndDeathQuery($sex); 8108add1155SRico Sonntag 8118add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 81244cdc21eSGreg Roach $query 81344cdc21eSGreg Roach ->whereIn('birth.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 81444cdc21eSGreg Roach ->whereIn('death.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']); 81544cdc21eSGreg Roach 8168add1155SRico Sonntag if ($related === 'BIRT') { 81744cdc21eSGreg Roach $query->whereBetween('birth.d_year', [$year1, $year2]); 8188add1155SRico Sonntag } elseif ($related === 'DEAT') { 81944cdc21eSGreg Roach $query->whereBetween('death.d_year', [$year1, $year2]); 8208add1155SRico Sonntag } 8218add1155SRico Sonntag } 8228add1155SRico Sonntag 82344cdc21eSGreg Roach return $query 824a69f5655SGreg Roach ->select(new Expression($prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1 AS days')) 825c8c87812SRico Sonntag ->orderBy('days', 'desc') 82644cdc21eSGreg Roach ->get() 82744cdc21eSGreg Roach ->all(); 8288add1155SRico Sonntag } 8298add1155SRico Sonntag 8308add1155SRico Sonntag /** 8318add1155SRico Sonntag * General query on ages. 8328add1155SRico Sonntag * 8338add1155SRico Sonntag * @return string 8348add1155SRico Sonntag */ 83588de55fdSRico Sonntag public function statsAge(): string 8368add1155SRico Sonntag { 837f78da678SGreg Roach return (new ChartAge($this->century_service, $this->tree))->chartAge(); 8388add1155SRico Sonntag } 8398add1155SRico Sonntag 8408add1155SRico Sonntag /** 8418add1155SRico Sonntag * Lifespan 8428add1155SRico Sonntag * 8438add1155SRico Sonntag * @param string $type 8448add1155SRico Sonntag * @param string $sex 8458add1155SRico Sonntag * 8468add1155SRico Sonntag * @return string 8478add1155SRico Sonntag */ 8488add1155SRico Sonntag private function longlifeQuery(string $type, string $sex): string 8498add1155SRico Sonntag { 85044cdc21eSGreg Roach $prefix = DB::connection()->getTablePrefix(); 8518add1155SRico Sonntag 85244cdc21eSGreg Roach $row = $this->birthAndDeathQuery($sex) 85344cdc21eSGreg Roach ->orderBy('days', 'desc') 854a69f5655SGreg Roach ->select(['individuals.*', new Expression($prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1 AS days')]) 85544cdc21eSGreg Roach ->first(); 85644cdc21eSGreg Roach 85744cdc21eSGreg Roach if ($row === null) { 8588add1155SRico Sonntag return ''; 8598add1155SRico Sonntag } 86044cdc21eSGreg Roach 86144cdc21eSGreg Roach /** @var Individual $individual */ 8626b9cb339SGreg Roach $individual = Registry::individualFactory()->mapper($this->tree)($row); 86344cdc21eSGreg Roach 864747d54c2SGreg Roach if ($type !== 'age' && !$individual->canShow()) { 86544cdc21eSGreg Roach return I18N::translate('This information is private and cannot be shown.'); 86644cdc21eSGreg Roach } 86744cdc21eSGreg Roach 8688add1155SRico Sonntag switch ($type) { 8698add1155SRico Sonntag default: 8708add1155SRico Sonntag case 'full': 87144cdc21eSGreg Roach return $individual->formatList(); 8728add1155SRico Sonntag 87344cdc21eSGreg Roach case 'age': 87444cdc21eSGreg Roach return I18N::number((int) ($row->days / 365.25)); 87544cdc21eSGreg Roach 87644cdc21eSGreg Roach case 'name': 87739ca88baSGreg Roach return '<a href="' . e($individual->url()) . '">' . $individual->fullName() . '</a>'; 87844cdc21eSGreg Roach } 8798add1155SRico Sonntag } 8808add1155SRico Sonntag 8818add1155SRico Sonntag /** 8828add1155SRico Sonntag * Find the longest lived individual. 8838add1155SRico Sonntag * 8848add1155SRico Sonntag * @return string 8858add1155SRico Sonntag */ 8868add1155SRico Sonntag public function longestLife(): string 8878add1155SRico Sonntag { 8888add1155SRico Sonntag return $this->longlifeQuery('full', 'BOTH'); 8898add1155SRico Sonntag } 8908add1155SRico Sonntag 8918add1155SRico Sonntag /** 8928add1155SRico Sonntag * Find the age of the longest lived individual. 8938add1155SRico Sonntag * 8948add1155SRico Sonntag * @return string 8958add1155SRico Sonntag */ 8968add1155SRico Sonntag public function longestLifeAge(): string 8978add1155SRico Sonntag { 8988add1155SRico Sonntag return $this->longlifeQuery('age', 'BOTH'); 8998add1155SRico Sonntag } 9008add1155SRico Sonntag 9018add1155SRico Sonntag /** 9028add1155SRico Sonntag * Find the name of the longest lived individual. 9038add1155SRico Sonntag * 9048add1155SRico Sonntag * @return string 9058add1155SRico Sonntag */ 9068add1155SRico Sonntag public function longestLifeName(): string 9078add1155SRico Sonntag { 9088add1155SRico Sonntag return $this->longlifeQuery('name', 'BOTH'); 9098add1155SRico Sonntag } 9108add1155SRico Sonntag 9118add1155SRico Sonntag /** 9128add1155SRico Sonntag * Find the longest lived female. 9138add1155SRico Sonntag * 9148add1155SRico Sonntag * @return string 9158add1155SRico Sonntag */ 9168add1155SRico Sonntag public function longestLifeFemale(): string 9178add1155SRico Sonntag { 9188add1155SRico Sonntag return $this->longlifeQuery('full', 'F'); 9198add1155SRico Sonntag } 9208add1155SRico Sonntag 9218add1155SRico Sonntag /** 9228add1155SRico Sonntag * Find the age of the longest lived female. 9238add1155SRico Sonntag * 9248add1155SRico Sonntag * @return string 9258add1155SRico Sonntag */ 9268add1155SRico Sonntag public function longestLifeFemaleAge(): string 9278add1155SRico Sonntag { 9288add1155SRico Sonntag return $this->longlifeQuery('age', 'F'); 9298add1155SRico Sonntag } 9308add1155SRico Sonntag 9318add1155SRico Sonntag /** 9328add1155SRico Sonntag * Find the name of the longest lived female. 9338add1155SRico Sonntag * 9348add1155SRico Sonntag * @return string 9358add1155SRico Sonntag */ 9368add1155SRico Sonntag public function longestLifeFemaleName(): string 9378add1155SRico Sonntag { 9388add1155SRico Sonntag return $this->longlifeQuery('name', 'F'); 9398add1155SRico Sonntag } 9408add1155SRico Sonntag 9418add1155SRico Sonntag /** 9428add1155SRico Sonntag * Find the longest lived male. 9438add1155SRico Sonntag * 9448add1155SRico Sonntag * @return string 9458add1155SRico Sonntag */ 9468add1155SRico Sonntag public function longestLifeMale(): string 9478add1155SRico Sonntag { 9488add1155SRico Sonntag return $this->longlifeQuery('full', 'M'); 9498add1155SRico Sonntag } 9508add1155SRico Sonntag 9518add1155SRico Sonntag /** 9528add1155SRico Sonntag * Find the age of the longest lived male. 9538add1155SRico Sonntag * 9548add1155SRico Sonntag * @return string 9558add1155SRico Sonntag */ 9568add1155SRico Sonntag public function longestLifeMaleAge(): string 9578add1155SRico Sonntag { 9588add1155SRico Sonntag return $this->longlifeQuery('age', 'M'); 9598add1155SRico Sonntag } 9608add1155SRico Sonntag 9618add1155SRico Sonntag /** 9628add1155SRico Sonntag * Find the name of the longest lived male. 9638add1155SRico Sonntag * 9648add1155SRico Sonntag * @return string 9658add1155SRico Sonntag */ 9668add1155SRico Sonntag public function longestLifeMaleName(): string 9678add1155SRico Sonntag { 9688add1155SRico Sonntag return $this->longlifeQuery('name', 'M'); 9698add1155SRico Sonntag } 9708add1155SRico Sonntag 9718add1155SRico Sonntag /** 9728add1155SRico Sonntag * Returns the calculated age the time of event. 9738add1155SRico Sonntag * 974054771e9SGreg Roach * @param int $days The age from the database record 9758add1155SRico Sonntag * 9768add1155SRico Sonntag * @return string 9778add1155SRico Sonntag */ 978054771e9SGreg Roach private function calculateAge(int $days): string 9798add1155SRico Sonntag { 980054771e9SGreg Roach if ($days < 31) { 981054771e9SGreg Roach return I18N::plural('%s day', '%s days', $days, I18N::number($days)); 9828add1155SRico Sonntag } 9838add1155SRico Sonntag 984054771e9SGreg Roach if ($days < 365) { 985054771e9SGreg Roach $months = (int) ($days / 30.5); 9861061e22bSGreg Roach return I18N::plural('%s month', '%s months', $months, I18N::number($months)); 987054771e9SGreg Roach } 988054771e9SGreg Roach 989054771e9SGreg Roach $years = (int) ($days / 365.25); 990054771e9SGreg Roach 991054771e9SGreg Roach return I18N::plural('%s year', '%s years', $years, I18N::number($years)); 9928add1155SRico Sonntag } 9938add1155SRico Sonntag 9948add1155SRico Sonntag /** 9958add1155SRico Sonntag * Find the oldest individuals. 9968add1155SRico Sonntag * 9978add1155SRico Sonntag * @param string $sex 9988add1155SRico Sonntag * @param int $total 9998add1155SRico Sonntag * 100091c84b80SGreg Roach * @return array<array<string,mixed>> 10018add1155SRico Sonntag */ 10028add1155SRico Sonntag private function topTenOldestQuery(string $sex, int $total): array 10038add1155SRico Sonntag { 100444cdc21eSGreg Roach $prefix = DB::connection()->getTablePrefix(); 10058add1155SRico Sonntag 100644cdc21eSGreg Roach $rows = $this->birthAndDeathQuery($sex) 100744cdc21eSGreg Roach ->groupBy(['i_id', 'i_file']) 100844cdc21eSGreg Roach ->orderBy('days', 'desc') 1009a69f5655SGreg Roach ->select(['individuals.*', new Expression('MAX(' . $prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1) AS days')]) 101044cdc21eSGreg Roach ->take($total) 101144cdc21eSGreg Roach ->get(); 10128add1155SRico Sonntag 10138add1155SRico Sonntag $top10 = []; 10148add1155SRico Sonntag foreach ($rows as $row) { 101544cdc21eSGreg Roach /** @var Individual $individual */ 10166b9cb339SGreg Roach $individual = Registry::individualFactory()->mapper($this->tree)($row); 10178add1155SRico Sonntag 101844cdc21eSGreg Roach if ($individual->canShow()) { 10198add1155SRico Sonntag $top10[] = [ 102044cdc21eSGreg Roach 'person' => $individual, 102144cdc21eSGreg Roach 'age' => $this->calculateAge((int) $row->days), 10228add1155SRico Sonntag ]; 10238add1155SRico Sonntag } 10248add1155SRico Sonntag } 10258add1155SRico Sonntag 10268add1155SRico Sonntag return $top10; 10278add1155SRico Sonntag } 10288add1155SRico Sonntag 10298add1155SRico Sonntag /** 10308add1155SRico Sonntag * Find the oldest individuals. 10318add1155SRico Sonntag * 10328add1155SRico Sonntag * @param int $total 10338add1155SRico Sonntag * 10348add1155SRico Sonntag * @return string 10358add1155SRico Sonntag */ 10368add1155SRico Sonntag public function topTenOldest(int $total = 10): string 10378add1155SRico Sonntag { 10388add1155SRico Sonntag $records = $this->topTenOldestQuery('BOTH', $total); 10398add1155SRico Sonntag 1040c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 10418add1155SRico Sonntag 'records' => $records, 1042c0112ce8SGreg Roach ]); 10438add1155SRico Sonntag } 10448add1155SRico Sonntag 10458add1155SRico Sonntag /** 10468add1155SRico Sonntag * Find the oldest living individuals. 10478add1155SRico Sonntag * 10488add1155SRico Sonntag * @param int $total 10498add1155SRico Sonntag * 10508add1155SRico Sonntag * @return string 10518add1155SRico Sonntag */ 10528add1155SRico Sonntag public function topTenOldestList(int $total = 10): string 10538add1155SRico Sonntag { 10548add1155SRico Sonntag $records = $this->topTenOldestQuery('BOTH', $total); 10558add1155SRico Sonntag 1056c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 10578add1155SRico Sonntag 'records' => $records, 1058c0112ce8SGreg Roach ]); 10598add1155SRico Sonntag } 10608add1155SRico Sonntag 10618add1155SRico Sonntag /** 10628add1155SRico Sonntag * Find the oldest females. 10638add1155SRico Sonntag * 10648add1155SRico Sonntag * @param int $total 10658add1155SRico Sonntag * 10668add1155SRico Sonntag * @return string 10678add1155SRico Sonntag */ 10688add1155SRico Sonntag public function topTenOldestFemale(int $total = 10): string 10698add1155SRico Sonntag { 10708add1155SRico Sonntag $records = $this->topTenOldestQuery('F', $total); 10718add1155SRico Sonntag 1072c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 10738add1155SRico Sonntag 'records' => $records, 1074c0112ce8SGreg Roach ]); 10758add1155SRico Sonntag } 10768add1155SRico Sonntag 10778add1155SRico Sonntag /** 10788add1155SRico Sonntag * Find the oldest living females. 10798add1155SRico Sonntag * 10808add1155SRico Sonntag * @param int $total 10818add1155SRico Sonntag * 10828add1155SRico Sonntag * @return string 10838add1155SRico Sonntag */ 10848add1155SRico Sonntag public function topTenOldestFemaleList(int $total = 10): string 10858add1155SRico Sonntag { 10868add1155SRico Sonntag $records = $this->topTenOldestQuery('F', $total); 10878add1155SRico Sonntag 1088c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 10898add1155SRico Sonntag 'records' => $records, 1090c0112ce8SGreg Roach ]); 10918add1155SRico Sonntag } 10928add1155SRico Sonntag 10938add1155SRico Sonntag /** 10948add1155SRico Sonntag * Find the longest lived males. 10958add1155SRico Sonntag * 10968add1155SRico Sonntag * @param int $total 10978add1155SRico Sonntag * 10988add1155SRico Sonntag * @return string 10998add1155SRico Sonntag */ 11008add1155SRico Sonntag public function topTenOldestMale(int $total = 10): string 11018add1155SRico Sonntag { 11028add1155SRico Sonntag $records = $this->topTenOldestQuery('M', $total); 11038add1155SRico Sonntag 1104c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 11058add1155SRico Sonntag 'records' => $records, 1106c0112ce8SGreg Roach ]); 11078add1155SRico Sonntag } 11088add1155SRico Sonntag 11098add1155SRico Sonntag /** 11108add1155SRico Sonntag * Find the longest lived males. 11118add1155SRico Sonntag * 11128add1155SRico Sonntag * @param int $total 11138add1155SRico Sonntag * 11148add1155SRico Sonntag * @return string 11158add1155SRico Sonntag */ 11168add1155SRico Sonntag public function topTenOldestMaleList(int $total = 10): string 11178add1155SRico Sonntag { 11188add1155SRico Sonntag $records = $this->topTenOldestQuery('M', $total); 11198add1155SRico Sonntag 1120c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 11218add1155SRico Sonntag 'records' => $records, 1122c0112ce8SGreg Roach ]); 11238add1155SRico Sonntag } 11248add1155SRico Sonntag 11258add1155SRico Sonntag /** 11268add1155SRico Sonntag * Find the oldest living individuals. 11278add1155SRico Sonntag * 1128c0112ce8SGreg Roach * @param string $sex "M", "F" or "BOTH" 11298add1155SRico Sonntag * @param int $total 11308add1155SRico Sonntag * 113191c84b80SGreg Roach * @return array<array<string,mixed>> 11328add1155SRico Sonntag */ 1133c0112ce8SGreg Roach private function topTenOldestAliveQuery(string $sex, int $total): array 11348add1155SRico Sonntag { 1135d1a467e4SGreg Roach $query = DB::table('dates') 11360b5fd0a6SGreg Roach ->join('individuals', static function (JoinClause $join): void { 1137d1a467e4SGreg Roach $join 1138d1a467e4SGreg Roach ->on('i_id', '=', 'd_gid') 1139d1a467e4SGreg Roach ->on('i_file', '=', 'd_file'); 1140d1a467e4SGreg Roach }) 1141d1a467e4SGreg Roach ->where('d_file', '=', $this->tree->id()) 1142d1a467e4SGreg Roach ->where('d_julianday1', '<>', 0) 1143d1a467e4SGreg Roach ->where('d_fact', '=', 'BIRT') 1144d1a467e4SGreg Roach ->where('i_gedcom', 'NOT LIKE', "%\n1 DEAT%") 1145d1a467e4SGreg Roach ->where('i_gedcom', 'NOT LIKE', "%\n1 BURI%") 1146d1a467e4SGreg Roach ->where('i_gedcom', 'NOT LIKE', "%\n1 CREM%"); 1147d1a467e4SGreg Roach 1148d1a467e4SGreg Roach if ($sex === 'F' || $sex === 'M') { 1149d1a467e4SGreg Roach $query->where('i_sex', '=', $sex); 11508add1155SRico Sonntag } 11518add1155SRico Sonntag 1152c0112ce8SGreg Roach return $query 1153d1a467e4SGreg Roach ->groupBy(['i_id', 'i_file']) 1154a69f5655SGreg Roach ->orderBy(new Expression('MIN(d_julianday1)')) 115547256fc5SGreg Roach ->select(['individuals.*']) 1156d1a467e4SGreg Roach ->take($total) 1157d1a467e4SGreg Roach ->get() 11586b9cb339SGreg Roach ->map(Registry::individualFactory()->mapper($this->tree)) 1159c0112ce8SGreg Roach ->filter(GedcomRecord::accessFilter()) 1160c0112ce8SGreg Roach ->map(function (Individual $individual): array { 1161c0112ce8SGreg Roach return [ 1162d1a467e4SGreg Roach 'person' => $individual, 1163d97083feSGreg Roach 'age' => $this->calculateAge(Registry::timestampFactory()->now()->julianDay() - $individual->getBirthDate()->minimumJulianDay()), 11648add1155SRico Sonntag ]; 1165c0112ce8SGreg Roach }) 1166c0112ce8SGreg Roach ->all(); 11678add1155SRico Sonntag } 11688add1155SRico Sonntag 11698add1155SRico Sonntag /** 11708add1155SRico Sonntag * Find the oldest living individuals. 11718add1155SRico Sonntag * 11728add1155SRico Sonntag * @param int $total 11738add1155SRico Sonntag * 11748add1155SRico Sonntag * @return string 11758add1155SRico Sonntag */ 11768add1155SRico Sonntag public function topTenOldestAlive(int $total = 10): string 11778add1155SRico Sonntag { 11788add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 11798add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 11808add1155SRico Sonntag } 11818add1155SRico Sonntag 11828add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('BOTH', $total); 11838add1155SRico Sonntag 1184c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 11858add1155SRico Sonntag 'records' => $records, 1186c0112ce8SGreg Roach ]); 11878add1155SRico Sonntag } 11888add1155SRico Sonntag 11898add1155SRico Sonntag /** 11908add1155SRico Sonntag * Find the oldest living individuals. 11918add1155SRico Sonntag * 11928add1155SRico Sonntag * @param int $total 11938add1155SRico Sonntag * 11948add1155SRico Sonntag * @return string 11958add1155SRico Sonntag */ 11968add1155SRico Sonntag public function topTenOldestListAlive(int $total = 10): string 11978add1155SRico Sonntag { 11988add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 11998add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 12008add1155SRico Sonntag } 12018add1155SRico Sonntag 12028add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('BOTH', $total); 12038add1155SRico Sonntag 1204c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 12058add1155SRico Sonntag 'records' => $records, 1206c0112ce8SGreg Roach ]); 12078add1155SRico Sonntag } 12088add1155SRico Sonntag 12098add1155SRico Sonntag /** 12108add1155SRico Sonntag * Find the oldest living females. 12118add1155SRico Sonntag * 12128add1155SRico Sonntag * @param int $total 12138add1155SRico Sonntag * 12148add1155SRico Sonntag * @return string 12158add1155SRico Sonntag */ 12168add1155SRico Sonntag public function topTenOldestFemaleAlive(int $total = 10): string 12178add1155SRico Sonntag { 12188add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 12198add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 12208add1155SRico Sonntag } 12218add1155SRico Sonntag 12228add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('F', $total); 12238add1155SRico Sonntag 1224c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 12258add1155SRico Sonntag 'records' => $records, 1226c0112ce8SGreg Roach ]); 12278add1155SRico Sonntag } 12288add1155SRico Sonntag 12298add1155SRico Sonntag /** 12308add1155SRico Sonntag * Find the oldest living females. 12318add1155SRico Sonntag * 12328add1155SRico Sonntag * @param int $total 12338add1155SRico Sonntag * 12348add1155SRico Sonntag * @return string 12358add1155SRico Sonntag */ 12368add1155SRico Sonntag public function topTenOldestFemaleListAlive(int $total = 10): string 12378add1155SRico Sonntag { 12388add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 12398add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 12408add1155SRico Sonntag } 12418add1155SRico Sonntag 12428add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('F', $total); 12438add1155SRico Sonntag 1244c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 12458add1155SRico Sonntag 'records' => $records, 1246c0112ce8SGreg Roach ]); 12478add1155SRico Sonntag } 12488add1155SRico Sonntag 12498add1155SRico Sonntag /** 12508add1155SRico Sonntag * Find the longest lived living males. 12518add1155SRico Sonntag * 12528add1155SRico Sonntag * @param int $total 12538add1155SRico Sonntag * 12548add1155SRico Sonntag * @return string 12558add1155SRico Sonntag */ 12568add1155SRico Sonntag public function topTenOldestMaleAlive(int $total = 10): string 12578add1155SRico Sonntag { 12588add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 12598add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 12608add1155SRico Sonntag } 12618add1155SRico Sonntag 12628add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('M', $total); 12638add1155SRico Sonntag 1264c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 12658add1155SRico Sonntag 'records' => $records, 1266c0112ce8SGreg Roach ]); 12678add1155SRico Sonntag } 12688add1155SRico Sonntag 12698add1155SRico Sonntag /** 12708add1155SRico Sonntag * Find the longest lived living males. 12718add1155SRico Sonntag * 12728add1155SRico Sonntag * @param int $total 12738add1155SRico Sonntag * 12748add1155SRico Sonntag * @return string 12758add1155SRico Sonntag */ 12768add1155SRico Sonntag public function topTenOldestMaleListAlive(int $total = 10): string 12778add1155SRico Sonntag { 12788add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 12798add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 12808add1155SRico Sonntag } 12818add1155SRico Sonntag 12828add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('M', $total); 12838add1155SRico Sonntag 1284c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 12858add1155SRico Sonntag 'records' => $records, 1286c0112ce8SGreg Roach ]); 12878add1155SRico Sonntag } 12888add1155SRico Sonntag 12898add1155SRico Sonntag /** 12908add1155SRico Sonntag * Find the average lifespan. 12918add1155SRico Sonntag * 1292c0112ce8SGreg Roach * @param string $sex "M", "F" or "BOTH" 12938add1155SRico Sonntag * @param bool $show_years 12948add1155SRico Sonntag * 12958add1155SRico Sonntag * @return string 12968add1155SRico Sonntag */ 1297c0112ce8SGreg Roach private function averageLifespanQuery(string $sex, bool $show_years): string 12988add1155SRico Sonntag { 129944cdc21eSGreg Roach $prefix = DB::connection()->getTablePrefix(); 13008add1155SRico Sonntag 130144cdc21eSGreg Roach $days = (int) $this->birthAndDeathQuery($sex) 1302a69f5655SGreg Roach ->select(new Expression('AVG(' . $prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1) AS days')) 130344cdc21eSGreg Roach ->value('days'); 13048add1155SRico Sonntag 13058add1155SRico Sonntag if ($show_years) { 130644cdc21eSGreg Roach return $this->calculateAge($days); 13078add1155SRico Sonntag } 13088add1155SRico Sonntag 130944cdc21eSGreg Roach return I18N::number((int) ($days / 365.25)); 13108add1155SRico Sonntag } 13118add1155SRico Sonntag 13128add1155SRico Sonntag /** 13138add1155SRico Sonntag * Find the average lifespan. 13148add1155SRico Sonntag * 13158add1155SRico Sonntag * @param bool $show_years 13168add1155SRico Sonntag * 13178add1155SRico Sonntag * @return string 13188add1155SRico Sonntag */ 131973d58381SGreg Roach public function averageLifespan(bool $show_years): string 13208add1155SRico Sonntag { 13218add1155SRico Sonntag return $this->averageLifespanQuery('BOTH', $show_years); 13228add1155SRico Sonntag } 13238add1155SRico Sonntag 13248add1155SRico Sonntag /** 13258add1155SRico Sonntag * Find the average lifespan of females. 13268add1155SRico Sonntag * 13278add1155SRico Sonntag * @param bool $show_years 13288add1155SRico Sonntag * 13298add1155SRico Sonntag * @return string 13308add1155SRico Sonntag */ 133173d58381SGreg Roach public function averageLifespanFemale(bool $show_years): string 13328add1155SRico Sonntag { 13338add1155SRico Sonntag return $this->averageLifespanQuery('F', $show_years); 13348add1155SRico Sonntag } 13358add1155SRico Sonntag 13368add1155SRico Sonntag /** 13378add1155SRico Sonntag * Find the average male lifespan. 13388add1155SRico Sonntag * 13398add1155SRico Sonntag * @param bool $show_years 13408add1155SRico Sonntag * 13418add1155SRico Sonntag * @return string 13428add1155SRico Sonntag */ 134373d58381SGreg Roach public function averageLifespanMale(bool $show_years): string 13448add1155SRico Sonntag { 13458add1155SRico Sonntag return $this->averageLifespanQuery('M', $show_years); 13468add1155SRico Sonntag } 13478add1155SRico Sonntag 13488add1155SRico Sonntag /** 13498add1155SRico Sonntag * Convert totals into percentages. 13508add1155SRico Sonntag * 13518add1155SRico Sonntag * @param int $count 13528add1155SRico Sonntag * @param int $total 13538add1155SRico Sonntag * 13548add1155SRico Sonntag * @return string 13558add1155SRico Sonntag */ 13568add1155SRico Sonntag private function getPercentage(int $count, int $total): string 13578add1155SRico Sonntag { 1358f78da678SGreg Roach return $total !== 0 ? I18N::percentage($count / $total, 1) : ''; 13598add1155SRico Sonntag } 13608add1155SRico Sonntag 13618add1155SRico Sonntag /** 13628add1155SRico Sonntag * Returns how many individuals exist in the tree. 13638add1155SRico Sonntag * 13648add1155SRico Sonntag * @return int 13658add1155SRico Sonntag */ 13668add1155SRico Sonntag private function totalIndividualsQuery(): int 13678add1155SRico Sonntag { 13688add1155SRico Sonntag return DB::table('individuals') 13698add1155SRico Sonntag ->where('i_file', '=', $this->tree->id()) 13708add1155SRico Sonntag ->count(); 13718add1155SRico Sonntag } 13728add1155SRico Sonntag 13738add1155SRico Sonntag /** 13748add1155SRico Sonntag * Count the number of living individuals. 13758add1155SRico Sonntag * 13768add1155SRico Sonntag * The totalLiving/totalDeceased queries assume that every dead person will 13778add1155SRico Sonntag * have a DEAT record. It will not include individuals who were born more 13788add1155SRico Sonntag * than MAX_ALIVE_AGE years ago, and who have no DEAT record. 13798add1155SRico Sonntag * A good reason to run the “Add missing DEAT records” batch-update! 13808add1155SRico Sonntag * 13818add1155SRico Sonntag * @return int 13828add1155SRico Sonntag */ 13838add1155SRico Sonntag private function totalLivingQuery(): int 13848add1155SRico Sonntag { 13853dc8167dSGreg Roach $query = DB::table('individuals') 13863dc8167dSGreg Roach ->where('i_file', '=', $this->tree->id()); 13873dc8167dSGreg Roach 13883dc8167dSGreg Roach foreach (Gedcom::DEATH_EVENTS as $death_event) { 138968d9d7c9SRico Sonntag $query->where('i_gedcom', 'NOT LIKE', "%\n1 " . $death_event . '%'); 13903dc8167dSGreg Roach } 13913dc8167dSGreg Roach 13923dc8167dSGreg Roach return $query->count(); 13938add1155SRico Sonntag } 13948add1155SRico Sonntag 13958add1155SRico Sonntag /** 13968add1155SRico Sonntag * Count the number of dead individuals. 13978add1155SRico Sonntag * 13988add1155SRico Sonntag * @return int 13998add1155SRico Sonntag */ 14008add1155SRico Sonntag private function totalDeceasedQuery(): int 14018add1155SRico Sonntag { 14028add1155SRico Sonntag return DB::table('individuals') 14038add1155SRico Sonntag ->where('i_file', '=', $this->tree->id()) 14040b5fd0a6SGreg Roach ->where(static function (Builder $query): void { 14053dc8167dSGreg Roach foreach (Gedcom::DEATH_EVENTS as $death_event) { 140668d9d7c9SRico Sonntag $query->orWhere('i_gedcom', 'LIKE', "%\n1 " . $death_event . '%'); 14073dc8167dSGreg Roach } 14083dc8167dSGreg Roach }) 14098add1155SRico Sonntag ->count(); 14108add1155SRico Sonntag } 14118add1155SRico Sonntag 14128add1155SRico Sonntag /** 14138add1155SRico Sonntag * Returns the total count of a specific sex. 14148add1155SRico Sonntag * 14158add1155SRico Sonntag * @param string $sex The sex to query 14168add1155SRico Sonntag * 14178add1155SRico Sonntag * @return int 14188add1155SRico Sonntag */ 14198add1155SRico Sonntag private function getTotalSexQuery(string $sex): int 14208add1155SRico Sonntag { 14218add1155SRico Sonntag return DB::table('individuals') 14228add1155SRico Sonntag ->where('i_file', '=', $this->tree->id()) 14238add1155SRico Sonntag ->where('i_sex', '=', $sex) 14248add1155SRico Sonntag ->count(); 14258add1155SRico Sonntag } 14268add1155SRico Sonntag 14278add1155SRico Sonntag /** 14288add1155SRico Sonntag * Returns the total number of males. 14298add1155SRico Sonntag * 14308add1155SRico Sonntag * @return int 14318add1155SRico Sonntag */ 14328add1155SRico Sonntag private function totalSexMalesQuery(): int 14338add1155SRico Sonntag { 14348add1155SRico Sonntag return $this->getTotalSexQuery('M'); 14358add1155SRico Sonntag } 14368add1155SRico Sonntag 14378add1155SRico Sonntag /** 14388add1155SRico Sonntag * Returns the total number of females. 14398add1155SRico Sonntag * 14408add1155SRico Sonntag * @return int 14418add1155SRico Sonntag */ 14428add1155SRico Sonntag private function totalSexFemalesQuery(): int 14438add1155SRico Sonntag { 14448add1155SRico Sonntag return $this->getTotalSexQuery('F'); 14458add1155SRico Sonntag } 14468add1155SRico Sonntag 14478add1155SRico Sonntag /** 14488add1155SRico Sonntag * Returns the total number of individuals with unknown sex. 14498add1155SRico Sonntag * 14508add1155SRico Sonntag * @return int 14518add1155SRico Sonntag */ 14528add1155SRico Sonntag private function totalSexUnknownQuery(): int 14538add1155SRico Sonntag { 14548add1155SRico Sonntag return $this->getTotalSexQuery('U'); 14558add1155SRico Sonntag } 14568add1155SRico Sonntag 14578add1155SRico Sonntag /** 14588add1155SRico Sonntag * Count the total families. 14598add1155SRico Sonntag * 14608add1155SRico Sonntag * @return int 14618add1155SRico Sonntag */ 14628add1155SRico Sonntag private function totalFamiliesQuery(): int 14638add1155SRico Sonntag { 14648add1155SRico Sonntag return DB::table('families') 14658add1155SRico Sonntag ->where('f_file', '=', $this->tree->id()) 14668add1155SRico Sonntag ->count(); 14678add1155SRico Sonntag } 14688add1155SRico Sonntag 14698add1155SRico Sonntag /** 14708add1155SRico Sonntag * How many individuals have one or more sources. 14718add1155SRico Sonntag * 14728add1155SRico Sonntag * @return int 14738add1155SRico Sonntag */ 14748add1155SRico Sonntag private function totalIndisWithSourcesQuery(): int 14758add1155SRico Sonntag { 14768add1155SRico Sonntag return DB::table('individuals') 14778add1155SRico Sonntag ->select(['i_id']) 14788add1155SRico Sonntag ->distinct() 14790b5fd0a6SGreg Roach ->join('link', static function (JoinClause $join): void { 14808add1155SRico Sonntag $join->on('i_id', '=', 'l_from') 14818add1155SRico Sonntag ->on('i_file', '=', 'l_file'); 14828add1155SRico Sonntag }) 14838add1155SRico Sonntag ->where('l_file', '=', $this->tree->id()) 14848add1155SRico Sonntag ->where('l_type', '=', 'SOUR') 14858add1155SRico Sonntag ->count('i_id'); 14868add1155SRico Sonntag } 14878add1155SRico Sonntag 14888add1155SRico Sonntag /** 14898add1155SRico Sonntag * Count the families with source records. 14908add1155SRico Sonntag * 14918add1155SRico Sonntag * @return int 14928add1155SRico Sonntag */ 14938add1155SRico Sonntag private function totalFamsWithSourcesQuery(): int 14948add1155SRico Sonntag { 14958add1155SRico Sonntag return DB::table('families') 14968add1155SRico Sonntag ->select(['f_id']) 14978add1155SRico Sonntag ->distinct() 14980b5fd0a6SGreg Roach ->join('link', static function (JoinClause $join): void { 14998add1155SRico Sonntag $join->on('f_id', '=', 'l_from') 15008add1155SRico Sonntag ->on('f_file', '=', 'l_file'); 15018add1155SRico Sonntag }) 15028add1155SRico Sonntag ->where('l_file', '=', $this->tree->id()) 15038add1155SRico Sonntag ->where('l_type', '=', 'SOUR') 15048add1155SRico Sonntag ->count('f_id'); 15058add1155SRico Sonntag } 15068add1155SRico Sonntag 15078add1155SRico Sonntag /** 15088add1155SRico Sonntag * Count the number of repositories. 15098add1155SRico Sonntag * 15108add1155SRico Sonntag * @return int 15118add1155SRico Sonntag */ 15128add1155SRico Sonntag private function totalRepositoriesQuery(): int 15138add1155SRico Sonntag { 15148add1155SRico Sonntag return DB::table('other') 15158add1155SRico Sonntag ->where('o_file', '=', $this->tree->id()) 15168add1155SRico Sonntag ->where('o_type', '=', 'REPO') 15178add1155SRico Sonntag ->count(); 15188add1155SRico Sonntag } 15198add1155SRico Sonntag 15208add1155SRico Sonntag /** 15218add1155SRico Sonntag * Count the total number of sources. 15228add1155SRico Sonntag * 15238add1155SRico Sonntag * @return int 15248add1155SRico Sonntag */ 15258add1155SRico Sonntag private function totalSourcesQuery(): int 15268add1155SRico Sonntag { 15278add1155SRico Sonntag return DB::table('sources') 15288add1155SRico Sonntag ->where('s_file', '=', $this->tree->id()) 15298add1155SRico Sonntag ->count(); 15308add1155SRico Sonntag } 15318add1155SRico Sonntag 15328add1155SRico Sonntag /** 15338add1155SRico Sonntag * Count the number of notes. 15348add1155SRico Sonntag * 15358add1155SRico Sonntag * @return int 15368add1155SRico Sonntag */ 15378add1155SRico Sonntag private function totalNotesQuery(): int 15388add1155SRico Sonntag { 15398add1155SRico Sonntag return DB::table('other') 15408add1155SRico Sonntag ->where('o_file', '=', $this->tree->id()) 15418add1155SRico Sonntag ->where('o_type', '=', 'NOTE') 15428add1155SRico Sonntag ->count(); 15438add1155SRico Sonntag } 15448add1155SRico Sonntag 15458add1155SRico Sonntag /** 15469969300fSBogie * Count the total media. 15479969300fSBogie * 15489969300fSBogie * @return int 15499969300fSBogie */ 15509969300fSBogie private function totalMediaQuery(): int 15519969300fSBogie { 15529969300fSBogie return DB::table('media') 15539969300fSBogie ->where('m_file', '=', $this->tree->id()) 15549969300fSBogie ->count(); 15559969300fSBogie } 15569969300fSBogie 15579969300fSBogie /** 15588add1155SRico Sonntag * Returns the total number of records. 15598add1155SRico Sonntag * 15608add1155SRico Sonntag * @return int 15618add1155SRico Sonntag */ 15628add1155SRico Sonntag private function totalRecordsQuery(): int 15638add1155SRico Sonntag { 15648add1155SRico Sonntag return $this->totalIndividualsQuery() 15658add1155SRico Sonntag + $this->totalFamiliesQuery() 15669969300fSBogie + $this->totalMediaQuery() 15678add1155SRico Sonntag + $this->totalNotesQuery() 15688add1155SRico Sonntag + $this->totalRepositoriesQuery() 15698add1155SRico Sonntag + $this->totalSourcesQuery(); 15708add1155SRico Sonntag } 15718add1155SRico Sonntag 15728add1155SRico Sonntag /** 15730dcd9387SGreg Roach * @return string 15748add1155SRico Sonntag */ 15758add1155SRico Sonntag public function totalRecords(): string 15768add1155SRico Sonntag { 15778add1155SRico Sonntag return I18N::number($this->totalRecordsQuery()); 15788add1155SRico Sonntag } 15798add1155SRico Sonntag 15808add1155SRico Sonntag /** 15810dcd9387SGreg Roach * @return string 15828add1155SRico Sonntag */ 15838add1155SRico Sonntag public function totalIndividuals(): string 15848add1155SRico Sonntag { 15858add1155SRico Sonntag return I18N::number($this->totalIndividualsQuery()); 15868add1155SRico Sonntag } 15878add1155SRico Sonntag 15888add1155SRico Sonntag /** 15898add1155SRico Sonntag * Count the number of living individuals. 15908add1155SRico Sonntag * 15918add1155SRico Sonntag * @return string 15928add1155SRico Sonntag */ 15938add1155SRico Sonntag public function totalLiving(): string 15948add1155SRico Sonntag { 15958add1155SRico Sonntag return I18N::number($this->totalLivingQuery()); 15968add1155SRico Sonntag } 15978add1155SRico Sonntag 15988add1155SRico Sonntag /** 15998add1155SRico Sonntag * Count the number of dead individuals. 16008add1155SRico Sonntag * 16018add1155SRico Sonntag * @return string 16028add1155SRico Sonntag */ 16038add1155SRico Sonntag public function totalDeceased(): string 16048add1155SRico Sonntag { 16058add1155SRico Sonntag return I18N::number($this->totalDeceasedQuery()); 16068add1155SRico Sonntag } 16078add1155SRico Sonntag 16088add1155SRico Sonntag /** 16090dcd9387SGreg Roach * @return string 16108add1155SRico Sonntag */ 16118add1155SRico Sonntag public function totalSexMales(): string 16128add1155SRico Sonntag { 16138add1155SRico Sonntag return I18N::number($this->totalSexMalesQuery()); 16148add1155SRico Sonntag } 16158add1155SRico Sonntag 16168add1155SRico Sonntag /** 16170dcd9387SGreg Roach * @return string 16188add1155SRico Sonntag */ 16198add1155SRico Sonntag public function totalSexFemales(): string 16208add1155SRico Sonntag { 16218add1155SRico Sonntag return I18N::number($this->totalSexFemalesQuery()); 16228add1155SRico Sonntag } 16238add1155SRico Sonntag 16248add1155SRico Sonntag /** 16250dcd9387SGreg Roach * @return string 16268add1155SRico Sonntag */ 16278add1155SRico Sonntag public function totalSexUnknown(): string 16288add1155SRico Sonntag { 16298add1155SRico Sonntag return I18N::number($this->totalSexUnknownQuery()); 16308add1155SRico Sonntag } 16318add1155SRico Sonntag 16328add1155SRico Sonntag /** 16330dcd9387SGreg Roach * @return string 16348add1155SRico Sonntag */ 16358add1155SRico Sonntag public function totalFamilies(): string 16368add1155SRico Sonntag { 16378add1155SRico Sonntag return I18N::number($this->totalFamiliesQuery()); 16388add1155SRico Sonntag } 16398add1155SRico Sonntag 16408add1155SRico Sonntag /** 16418add1155SRico Sonntag * How many individuals have one or more sources. 16428add1155SRico Sonntag * 16438add1155SRico Sonntag * @return string 16448add1155SRico Sonntag */ 16458add1155SRico Sonntag public function totalIndisWithSources(): string 16468add1155SRico Sonntag { 16478add1155SRico Sonntag return I18N::number($this->totalIndisWithSourcesQuery()); 16488add1155SRico Sonntag } 16498add1155SRico Sonntag 16508add1155SRico Sonntag /** 16518add1155SRico Sonntag * Count the families with with source records. 16528add1155SRico Sonntag * 16538add1155SRico Sonntag * @return string 16548add1155SRico Sonntag */ 16558add1155SRico Sonntag public function totalFamsWithSources(): string 16568add1155SRico Sonntag { 16578add1155SRico Sonntag return I18N::number($this->totalFamsWithSourcesQuery()); 16588add1155SRico Sonntag } 16598add1155SRico Sonntag 16608add1155SRico Sonntag /** 16610dcd9387SGreg Roach * @return string 16628add1155SRico Sonntag */ 16638add1155SRico Sonntag public function totalRepositories(): string 16648add1155SRico Sonntag { 16658add1155SRico Sonntag return I18N::number($this->totalRepositoriesQuery()); 16668add1155SRico Sonntag } 16678add1155SRico Sonntag 16688add1155SRico Sonntag /** 16690dcd9387SGreg Roach * @return string 16708add1155SRico Sonntag */ 16718add1155SRico Sonntag public function totalSources(): string 16728add1155SRico Sonntag { 16738add1155SRico Sonntag return I18N::number($this->totalSourcesQuery()); 16748add1155SRico Sonntag } 16758add1155SRico Sonntag 16768add1155SRico Sonntag /** 16770dcd9387SGreg Roach * @return string 16788add1155SRico Sonntag */ 16798add1155SRico Sonntag public function totalNotes(): string 16808add1155SRico Sonntag { 16818add1155SRico Sonntag return I18N::number($this->totalNotesQuery()); 16828add1155SRico Sonntag } 16838add1155SRico Sonntag 16848add1155SRico Sonntag /** 16850dcd9387SGreg Roach * @return string 16868add1155SRico Sonntag */ 16878add1155SRico Sonntag public function totalIndividualsPercentage(): string 16888add1155SRico Sonntag { 16898add1155SRico Sonntag return $this->getPercentage( 16908add1155SRico Sonntag $this->totalIndividualsQuery(), 16918add1155SRico Sonntag $this->totalRecordsQuery() 16928add1155SRico Sonntag ); 16938add1155SRico Sonntag } 16948add1155SRico Sonntag 16958add1155SRico Sonntag /** 16960dcd9387SGreg Roach * @return string 16978add1155SRico Sonntag */ 1698*baa78b21STheDutchJewel public function totalIndisWithSourcesPercentage(): string 1699*baa78b21STheDutchJewel { 1700*baa78b21STheDutchJewel return $this->getPercentage( 1701*baa78b21STheDutchJewel $this->totalIndisWithSourcesQuery(), 1702*baa78b21STheDutchJewel $this->totalIndividualsQuery() 1703*baa78b21STheDutchJewel ); 1704*baa78b21STheDutchJewel } 1705*baa78b21STheDutchJewel 1706*baa78b21STheDutchJewel /** 1707*baa78b21STheDutchJewel * @return string 1708*baa78b21STheDutchJewel */ 17098add1155SRico Sonntag public function totalFamiliesPercentage(): string 17108add1155SRico Sonntag { 17118add1155SRico Sonntag return $this->getPercentage( 17128add1155SRico Sonntag $this->totalFamiliesQuery(), 17138add1155SRico Sonntag $this->totalRecordsQuery() 17148add1155SRico Sonntag ); 17158add1155SRico Sonntag } 17168add1155SRico Sonntag 17178add1155SRico Sonntag /** 17180dcd9387SGreg Roach * @return string 17198add1155SRico Sonntag */ 1720*baa78b21STheDutchJewel public function totalFamsWithSourcesPercentage(): string 1721*baa78b21STheDutchJewel { 1722*baa78b21STheDutchJewel return $this->getPercentage( 1723*baa78b21STheDutchJewel $this->totalFamsWithSourcesQuery(), 1724*baa78b21STheDutchJewel $this->totalFamiliesQuery() 1725*baa78b21STheDutchJewel ); 1726*baa78b21STheDutchJewel } 1727*baa78b21STheDutchJewel 1728*baa78b21STheDutchJewel /** 1729*baa78b21STheDutchJewel * @return string 1730*baa78b21STheDutchJewel */ 17318add1155SRico Sonntag public function totalRepositoriesPercentage(): string 17328add1155SRico Sonntag { 17338add1155SRico Sonntag return $this->getPercentage( 17348add1155SRico Sonntag $this->totalRepositoriesQuery(), 17358add1155SRico Sonntag $this->totalRecordsQuery() 17368add1155SRico Sonntag ); 17378add1155SRico Sonntag } 17388add1155SRico Sonntag 17398add1155SRico Sonntag /** 17400dcd9387SGreg Roach * @return string 17418add1155SRico Sonntag */ 17428add1155SRico Sonntag public function totalSourcesPercentage(): string 17438add1155SRico Sonntag { 17448add1155SRico Sonntag return $this->getPercentage( 17458add1155SRico Sonntag $this->totalSourcesQuery(), 17468add1155SRico Sonntag $this->totalRecordsQuery() 17478add1155SRico Sonntag ); 17488add1155SRico Sonntag } 17498add1155SRico Sonntag 17508add1155SRico Sonntag /** 17510dcd9387SGreg Roach * @return string 17528add1155SRico Sonntag */ 17538add1155SRico Sonntag public function totalNotesPercentage(): string 17548add1155SRico Sonntag { 17558add1155SRico Sonntag return $this->getPercentage( 17568add1155SRico Sonntag $this->totalNotesQuery(), 17578add1155SRico Sonntag $this->totalRecordsQuery() 17588add1155SRico Sonntag ); 17598add1155SRico Sonntag } 17608add1155SRico Sonntag 17618add1155SRico Sonntag /** 17620dcd9387SGreg Roach * @return string 17638add1155SRico Sonntag */ 17648add1155SRico Sonntag public function totalLivingPercentage(): string 17658add1155SRico Sonntag { 17668add1155SRico Sonntag return $this->getPercentage( 17678add1155SRico Sonntag $this->totalLivingQuery(), 17688add1155SRico Sonntag $this->totalIndividualsQuery() 17698add1155SRico Sonntag ); 17708add1155SRico Sonntag } 17718add1155SRico Sonntag 17728add1155SRico Sonntag /** 17730dcd9387SGreg Roach * @return string 17748add1155SRico Sonntag */ 17758add1155SRico Sonntag public function totalDeceasedPercentage(): string 17768add1155SRico Sonntag { 17778add1155SRico Sonntag return $this->getPercentage( 17788add1155SRico Sonntag $this->totalDeceasedQuery(), 17798add1155SRico Sonntag $this->totalIndividualsQuery() 17808add1155SRico Sonntag ); 17818add1155SRico Sonntag } 17828add1155SRico Sonntag 17838add1155SRico Sonntag /** 17840dcd9387SGreg Roach * @return string 17858add1155SRico Sonntag */ 17868add1155SRico Sonntag public function totalSexMalesPercentage(): string 17878add1155SRico Sonntag { 17888add1155SRico Sonntag return $this->getPercentage( 17898add1155SRico Sonntag $this->totalSexMalesQuery(), 17908add1155SRico Sonntag $this->totalIndividualsQuery() 17918add1155SRico Sonntag ); 17928add1155SRico Sonntag } 17938add1155SRico Sonntag 17948add1155SRico Sonntag /** 17950dcd9387SGreg Roach * @return string 17968add1155SRico Sonntag */ 17978add1155SRico Sonntag public function totalSexFemalesPercentage(): string 17988add1155SRico Sonntag { 17998add1155SRico Sonntag return $this->getPercentage( 18008add1155SRico Sonntag $this->totalSexFemalesQuery(), 18018add1155SRico Sonntag $this->totalIndividualsQuery() 18028add1155SRico Sonntag ); 18038add1155SRico Sonntag } 18048add1155SRico Sonntag 18058add1155SRico Sonntag /** 18060dcd9387SGreg Roach * @return string 18078add1155SRico Sonntag */ 18088add1155SRico Sonntag public function totalSexUnknownPercentage(): string 18098add1155SRico Sonntag { 18108add1155SRico Sonntag return $this->getPercentage( 18118add1155SRico Sonntag $this->totalSexUnknownQuery(), 18128add1155SRico Sonntag $this->totalIndividualsQuery() 18138add1155SRico Sonntag ); 18148add1155SRico Sonntag } 18158add1155SRico Sonntag 18168add1155SRico Sonntag /** 18178add1155SRico Sonntag * Create a chart of common given names. 18188add1155SRico Sonntag * 18198add1155SRico Sonntag * @param string|null $color_from 18208add1155SRico Sonntag * @param string|null $color_to 18218add1155SRico Sonntag * @param int $maxtoshow 18228add1155SRico Sonntag * 18238add1155SRico Sonntag * @return string 18248add1155SRico Sonntag */ 18258add1155SRico Sonntag public function chartCommonGiven( 18268add1155SRico Sonntag string $color_from = null, 18278add1155SRico Sonntag string $color_to = null, 18288add1155SRico Sonntag int $maxtoshow = 7 18298add1155SRico Sonntag ): string { 18308add1155SRico Sonntag $tot_indi = $this->totalIndividualsQuery(); 18318add1155SRico Sonntag $given = $this->commonGivenQuery('B', 'chart', false, 1, $maxtoshow); 18328add1155SRico Sonntag 1833320f6a24SGreg Roach if ($given === []) { 1834dd7dd2a1SRico Sonntag return I18N::translate('This information is not available.'); 183588de55fdSRico Sonntag } 183688de55fdSRico Sonntag 1837f78da678SGreg Roach return (new ChartCommonGiven($this->color_service)) 183888de55fdSRico Sonntag ->chartCommonGiven($tot_indi, $given, $color_from, $color_to); 18398add1155SRico Sonntag } 18408add1155SRico Sonntag 18418add1155SRico Sonntag /** 18428add1155SRico Sonntag * Create a chart of common surnames. 18438add1155SRico Sonntag * 18448add1155SRico Sonntag * @param string|null $color_from 18458add1155SRico Sonntag * @param string|null $color_to 18468add1155SRico Sonntag * @param int $number_of_surnames 18478add1155SRico Sonntag * 18488add1155SRico Sonntag * @return string 18498add1155SRico Sonntag */ 18508add1155SRico Sonntag public function chartCommonSurnames( 18518add1155SRico Sonntag string $color_from = null, 18528add1155SRico Sonntag string $color_to = null, 18538add1155SRico Sonntag int $number_of_surnames = 10 18548add1155SRico Sonntag ): string { 18558add1155SRico Sonntag $tot_indi = $this->totalIndividualsQuery(); 18568add1155SRico Sonntag $all_surnames = $this->topSurnames($number_of_surnames, 0); 18578add1155SRico Sonntag 1858320f6a24SGreg Roach if ($all_surnames === []) { 1859dd7dd2a1SRico Sonntag return I18N::translate('This information is not available.'); 186088de55fdSRico Sonntag } 186188de55fdSRico Sonntag 18627e128bbfSGreg Roach $surname_tradition = Registry::surnameTraditionFactory() 18637e128bbfSGreg Roach ->make($this->tree->getPreference('SURNAME_TRADITION')); 1864f78da678SGreg Roach 1865f78da678SGreg Roach return (new ChartCommonSurname($this->color_service, $surname_tradition)) 186688de55fdSRico Sonntag ->chartCommonSurnames($tot_indi, $all_surnames, $color_from, $color_to); 18678add1155SRico Sonntag } 18688add1155SRico Sonntag 18698add1155SRico Sonntag /** 18708add1155SRico Sonntag * Create a chart showing mortality. 18718add1155SRico Sonntag * 18728add1155SRico Sonntag * @param string|null $color_living 18738add1155SRico Sonntag * @param string|null $color_dead 18748add1155SRico Sonntag * 18758add1155SRico Sonntag * @return string 18768add1155SRico Sonntag */ 187788de55fdSRico Sonntag public function chartMortality(string $color_living = null, string $color_dead = null): string 18788add1155SRico Sonntag { 18798add1155SRico Sonntag $tot_l = $this->totalLivingQuery(); 18808add1155SRico Sonntag $tot_d = $this->totalDeceasedQuery(); 18818add1155SRico Sonntag 1882f78da678SGreg Roach return (new ChartMortality($this->color_service)) 188388de55fdSRico Sonntag ->chartMortality($tot_l, $tot_d, $color_living, $color_dead); 18848add1155SRico Sonntag } 18858add1155SRico Sonntag 18868add1155SRico Sonntag /** 18878add1155SRico Sonntag * Create a chart showing individuals with/without sources. 18888add1155SRico Sonntag * 18898add1155SRico Sonntag * @param string|null $color_from 18908add1155SRico Sonntag * @param string|null $color_to 18918add1155SRico Sonntag * 18928add1155SRico Sonntag * @return string 18938add1155SRico Sonntag */ 18948add1155SRico Sonntag public function chartIndisWithSources( 18958add1155SRico Sonntag string $color_from = null, 18968add1155SRico Sonntag string $color_to = null 18978add1155SRico Sonntag ): string { 18988add1155SRico Sonntag $tot_indi = $this->totalIndividualsQuery(); 18998add1155SRico Sonntag $tot_indi_source = $this->totalIndisWithSourcesQuery(); 19008add1155SRico Sonntag 1901f78da678SGreg Roach return (new ChartIndividualWithSources($this->color_service)) 190288de55fdSRico Sonntag ->chartIndisWithSources($tot_indi, $tot_indi_source, $color_from, $color_to); 19038add1155SRico Sonntag } 19048add1155SRico Sonntag 19058add1155SRico Sonntag /** 19068add1155SRico Sonntag * Create a chart of individuals with/without sources. 19078add1155SRico Sonntag * 19088add1155SRico Sonntag * @param string|null $color_from 19098add1155SRico Sonntag * @param string|null $color_to 19108add1155SRico Sonntag * 19118add1155SRico Sonntag * @return string 19128add1155SRico Sonntag */ 19138add1155SRico Sonntag public function chartFamsWithSources( 19148add1155SRico Sonntag string $color_from = null, 19158add1155SRico Sonntag string $color_to = null 19168add1155SRico Sonntag ): string { 19178add1155SRico Sonntag $tot_fam = $this->totalFamiliesQuery(); 19188add1155SRico Sonntag $tot_fam_source = $this->totalFamsWithSourcesQuery(); 19198add1155SRico Sonntag 1920f78da678SGreg Roach return (new ChartFamilyWithSources($this->color_service)) 192188de55fdSRico Sonntag ->chartFamsWithSources($tot_fam, $tot_fam_source, $color_from, $color_to); 19228add1155SRico Sonntag } 19238add1155SRico Sonntag 19248add1155SRico Sonntag /** 19250dcd9387SGreg Roach * @param string|null $color_female 19260dcd9387SGreg Roach * @param string|null $color_male 19270dcd9387SGreg Roach * @param string|null $color_unknown 19280dcd9387SGreg Roach * 19290dcd9387SGreg Roach * @return string 19308add1155SRico Sonntag */ 19318add1155SRico Sonntag public function chartSex( 19328add1155SRico Sonntag string $color_female = null, 19338add1155SRico Sonntag string $color_male = null, 19348add1155SRico Sonntag string $color_unknown = null 19358add1155SRico Sonntag ): string { 19368add1155SRico Sonntag $tot_m = $this->totalSexMalesQuery(); 19378add1155SRico Sonntag $tot_f = $this->totalSexFemalesQuery(); 19388add1155SRico Sonntag $tot_u = $this->totalSexUnknownQuery(); 19398add1155SRico Sonntag 194093ccd686SRico Sonntag return (new ChartSex()) 194188de55fdSRico Sonntag ->chartSex($tot_m, $tot_f, $tot_u, $color_female, $color_male, $color_unknown); 19428add1155SRico Sonntag } 194344cdc21eSGreg Roach 194444cdc21eSGreg Roach /** 194544cdc21eSGreg Roach * Query individuals, with their births and deaths. 194644cdc21eSGreg Roach * 194744cdc21eSGreg Roach * @param string $sex 194844cdc21eSGreg Roach * 194944cdc21eSGreg Roach * @return Builder 195044cdc21eSGreg Roach */ 1951e2cbf57aSGreg Roach private function birthAndDeathQuery(string $sex): Builder 1952e2cbf57aSGreg Roach { 195344cdc21eSGreg Roach $query = DB::table('individuals') 195444cdc21eSGreg Roach ->where('i_file', '=', $this->tree->id()) 19550b5fd0a6SGreg Roach ->join('dates AS birth', static function (JoinClause $join): void { 195644cdc21eSGreg Roach $join 195744cdc21eSGreg Roach ->on('birth.d_file', '=', 'i_file') 195844cdc21eSGreg Roach ->on('birth.d_gid', '=', 'i_id'); 195944cdc21eSGreg Roach }) 19600b5fd0a6SGreg Roach ->join('dates AS death', static function (JoinClause $join): void { 196144cdc21eSGreg Roach $join 196244cdc21eSGreg Roach ->on('death.d_file', '=', 'i_file') 196344cdc21eSGreg Roach ->on('death.d_gid', '=', 'i_id'); 196444cdc21eSGreg Roach }) 196544cdc21eSGreg Roach ->where('birth.d_fact', '=', 'BIRT') 196644cdc21eSGreg Roach ->where('death.d_fact', '=', 'DEAT') 196744cdc21eSGreg Roach ->whereColumn('death.d_julianday1', '>=', 'birth.d_julianday2') 196844cdc21eSGreg Roach ->where('birth.d_julianday2', '<>', 0); 196944cdc21eSGreg Roach 197044cdc21eSGreg Roach if ($sex === 'M' || $sex === 'F') { 197144cdc21eSGreg Roach $query->where('i_sex', '=', $sex); 197244cdc21eSGreg Roach } 197344cdc21eSGreg Roach 197444cdc21eSGreg Roach return $query; 197544cdc21eSGreg Roach } 19768add1155SRico Sonntag} 1977