18add1155SRico Sonntag<?php 23976b470SGreg Roach 38add1155SRico Sonntag/** 48add1155SRico Sonntag * webtrees: online genealogy 52da2e0a6SGreg Roach * Copyright (C) 2021 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; 234459dc9aSGreg Roachuse Fisharebest\Webtrees\Carbon; 246b9cb339SGreg Roachuse Fisharebest\Webtrees\Registry; 258add1155SRico Sonntaguse Fisharebest\Webtrees\Functions\FunctionsPrintLists; 268add1155SRico Sonntaguse Fisharebest\Webtrees\Gedcom; 27d1a467e4SGreg Roachuse Fisharebest\Webtrees\GedcomRecord; 288add1155SRico Sonntaguse Fisharebest\Webtrees\I18N; 298add1155SRico Sonntaguse Fisharebest\Webtrees\Individual; 3067992b6aSRichard Cisseeuse Fisharebest\Webtrees\Module\IndividualListModule; 3167992b6aSRichard Cisseeuse Fisharebest\Webtrees\Module\ModuleInterface; 3287cca37cSGreg Roachuse Fisharebest\Webtrees\Module\ModuleListInterface; 3367992b6aSRichard Cisseeuse Fisharebest\Webtrees\Services\ModuleService; 348add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartAge; 358add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartBirth; 368add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartCommonGiven; 378add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartCommonSurname; 388add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartDeath; 398add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartFamilyWithSources; 4088de55fdSRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartIndividualWithSources; 418add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartMortality; 428add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartSex; 438add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Repository\Interfaces\IndividualRepositoryInterface; 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; 4991c84b80SGreg Roachuse stdClass; 5091c84b80SGreg Roach 5171378461SGreg Roachuse function array_key_exists; 5271378461SGreg Roachuse function array_slice; 5371378461SGreg Roach 548add1155SRico Sonntag/** 558add1155SRico Sonntag * 568add1155SRico Sonntag */ 578add1155SRico Sonntagclass IndividualRepository implements IndividualRepositoryInterface 588add1155SRico Sonntag{ 598add1155SRico Sonntag /** 608add1155SRico Sonntag * @var Tree 618add1155SRico Sonntag */ 628add1155SRico Sonntag private $tree; 638add1155SRico Sonntag 648add1155SRico Sonntag /** 658add1155SRico Sonntag * Constructor. 668add1155SRico Sonntag * 678add1155SRico Sonntag * @param Tree $tree 688add1155SRico Sonntag */ 698add1155SRico Sonntag public function __construct(Tree $tree) 708add1155SRico Sonntag { 718add1155SRico Sonntag $this->tree = $tree; 728add1155SRico Sonntag } 738add1155SRico Sonntag 748add1155SRico Sonntag /** 758add1155SRico Sonntag * Find common given names. 768add1155SRico Sonntag * 778add1155SRico Sonntag * @param string $sex 788add1155SRico Sonntag * @param string $type 798add1155SRico Sonntag * @param bool $show_tot 808add1155SRico Sonntag * @param int $threshold 818add1155SRico Sonntag * @param int $maxtoshow 828add1155SRico Sonntag * 83*09482a55SGreg Roach * @return string|array<int> 848add1155SRico Sonntag */ 858add1155SRico Sonntag private function commonGivenQuery(string $sex, string $type, bool $show_tot, int $threshold, int $maxtoshow) 868add1155SRico Sonntag { 87d1a467e4SGreg Roach $query = DB::table('name') 880b5fd0a6SGreg Roach ->join('individuals', static function (JoinClause $join): void { 89d1a467e4SGreg Roach $join 90d1a467e4SGreg Roach ->on('i_file', '=', 'n_file') 91d1a467e4SGreg Roach ->on('i_id', '=', 'n_id'); 92d1a467e4SGreg Roach }) 93d1a467e4SGreg Roach ->where('n_file', '=', $this->tree->id()) 94d1a467e4SGreg Roach ->where('n_type', '<>', '_MARNM') 958fb4e87cSGreg Roach ->where('n_givn', '<>', Individual::PRAENOMEN_NESCIO) 96a69f5655SGreg Roach ->where(new Expression('LENGTH(n_givn)'), '>', 1); 97d1a467e4SGreg Roach 988add1155SRico Sonntag switch ($sex) { 998add1155SRico Sonntag case 'M': 1008add1155SRico Sonntag case 'F': 1018add1155SRico Sonntag case 'U': 102d1a467e4SGreg Roach $query->where('i_sex', '=', $sex); 1038add1155SRico Sonntag break; 104d1a467e4SGreg Roach 1058add1155SRico Sonntag case 'B': 1068add1155SRico Sonntag default: 107d1a467e4SGreg Roach $query->where('i_sex', '<>', 'U'); 1088add1155SRico Sonntag break; 1098add1155SRico Sonntag } 1108add1155SRico Sonntag 111d1a467e4SGreg Roach $rows = $query 1123413ec75SRico Sonntag ->groupBy(['n_givn']) 113a69f5655SGreg Roach ->select(['n_givn', new Expression('COUNT(distinct n_id) AS count')]) 114d1a467e4SGreg Roach ->pluck('count', 'n_givn'); 1158add1155SRico Sonntag 1168add1155SRico Sonntag $nameList = []; 1178add1155SRico Sonntag 118d1a467e4SGreg Roach foreach ($rows as $n_givn => $count) { 1198add1155SRico Sonntag // Split “John Thomas” into “John” and “Thomas” and count against both totals 12021f5b67aSGreg Roach foreach (explode(' ', (string) $n_givn) as $given) { 1218add1155SRico Sonntag // Exclude initials and particles. 1228add1155SRico Sonntag if (!preg_match('/^([A-Z]|[a-z]{1,3})$/', $given)) { 1236ccdf4f0SGreg Roach if (array_key_exists($given, $nameList)) { 124d1a467e4SGreg Roach $nameList[$given] += (int) $count; 1258add1155SRico Sonntag } else { 126d1a467e4SGreg Roach $nameList[$given] = (int) $count; 1278add1155SRico Sonntag } 1288add1155SRico Sonntag } 1298add1155SRico Sonntag } 1308add1155SRico Sonntag } 1318add1155SRico Sonntag arsort($nameList); 1326ccdf4f0SGreg Roach $nameList = array_slice($nameList, 0, $maxtoshow); 1338add1155SRico Sonntag 1348add1155SRico Sonntag foreach ($nameList as $given => $total) { 1358add1155SRico Sonntag if ($total < $threshold) { 1368add1155SRico Sonntag unset($nameList[$given]); 1378add1155SRico Sonntag } 1388add1155SRico Sonntag } 1398add1155SRico Sonntag 1408add1155SRico Sonntag switch ($type) { 1418add1155SRico Sonntag case 'chart': 1428add1155SRico Sonntag return $nameList; 1438add1155SRico Sonntag 1448add1155SRico Sonntag case 'table': 1458add1155SRico Sonntag return view('lists/given-names-table', [ 1468add1155SRico Sonntag 'given_names' => $nameList, 1478add1155SRico Sonntag ]); 1488add1155SRico Sonntag 1498add1155SRico Sonntag case 'list': 1508add1155SRico Sonntag return view('lists/given-names-list', [ 1518add1155SRico Sonntag 'given_names' => $nameList, 1528add1155SRico Sonntag 'show_totals' => $show_tot, 1538add1155SRico Sonntag ]); 1548add1155SRico Sonntag 1558add1155SRico Sonntag case 'nolist': 1568add1155SRico Sonntag default: 1570b5fd0a6SGreg Roach array_walk($nameList, static function (string &$value, string $key) use ($show_tot): void { 1588add1155SRico Sonntag if ($show_tot) { 159315eb316SGreg Roach $value = '<bdi>' . e($key) . '</bdi> (' . I18N::number((int) $value) . ')'; 1602da2e0a6SGreg Roach } else { 161315eb316SGreg Roach $value = '<bdi>' . e($key) . '</bdi>'; 1628add1155SRico Sonntag } 1638add1155SRico Sonntag }); 1648add1155SRico Sonntag 1658add1155SRico Sonntag return implode(I18N::$list_separator, $nameList); 1668add1155SRico Sonntag } 1678add1155SRico Sonntag } 1688add1155SRico Sonntag 1698add1155SRico Sonntag /** 1708add1155SRico Sonntag * Find common give names. 1718add1155SRico Sonntag * 1728add1155SRico Sonntag * @param int $threshold 1738add1155SRico Sonntag * @param int $maxtoshow 1748add1155SRico Sonntag * 1758add1155SRico Sonntag * @return string 1768add1155SRico Sonntag */ 1778add1155SRico Sonntag public function commonGiven(int $threshold = 1, int $maxtoshow = 10): string 1788add1155SRico Sonntag { 1798add1155SRico Sonntag return $this->commonGivenQuery('B', 'nolist', false, $threshold, $maxtoshow); 1808add1155SRico Sonntag } 1818add1155SRico Sonntag 1828add1155SRico Sonntag /** 1838add1155SRico Sonntag * Find common give names. 1848add1155SRico Sonntag * 1858add1155SRico Sonntag * @param int $threshold 1868add1155SRico Sonntag * @param int $maxtoshow 1878add1155SRico Sonntag * 1888add1155SRico Sonntag * @return string 1898add1155SRico Sonntag */ 1908add1155SRico Sonntag public function commonGivenTotals(int $threshold = 1, int $maxtoshow = 10): string 1918add1155SRico Sonntag { 1928add1155SRico Sonntag return $this->commonGivenQuery('B', 'nolist', true, $threshold, $maxtoshow); 1938add1155SRico Sonntag } 1948add1155SRico Sonntag 1958add1155SRico Sonntag /** 1968add1155SRico Sonntag * Find common give names. 1978add1155SRico Sonntag * 1988add1155SRico Sonntag * @param int $threshold 1998add1155SRico Sonntag * @param int $maxtoshow 2008add1155SRico Sonntag * 2018add1155SRico Sonntag * @return string 2028add1155SRico Sonntag */ 2038add1155SRico Sonntag public function commonGivenList(int $threshold = 1, int $maxtoshow = 10): string 2048add1155SRico Sonntag { 2058add1155SRico Sonntag return $this->commonGivenQuery('B', 'list', false, $threshold, $maxtoshow); 2068add1155SRico Sonntag } 2078add1155SRico Sonntag 2088add1155SRico Sonntag /** 2098add1155SRico Sonntag * Find common give names. 2108add1155SRico Sonntag * 2118add1155SRico Sonntag * @param int $threshold 2128add1155SRico Sonntag * @param int $maxtoshow 2138add1155SRico Sonntag * 2148add1155SRico Sonntag * @return string 2158add1155SRico Sonntag */ 2168add1155SRico Sonntag public function commonGivenListTotals(int $threshold = 1, int $maxtoshow = 10): string 2178add1155SRico Sonntag { 2188add1155SRico Sonntag return $this->commonGivenQuery('B', 'list', true, $threshold, $maxtoshow); 2198add1155SRico Sonntag } 2208add1155SRico Sonntag 2218add1155SRico Sonntag /** 2228add1155SRico Sonntag * Find common give names. 2238add1155SRico Sonntag * 2248add1155SRico Sonntag * @param int $threshold 2258add1155SRico Sonntag * @param int $maxtoshow 2268add1155SRico Sonntag * 2278add1155SRico Sonntag * @return string 2288add1155SRico Sonntag */ 2298add1155SRico Sonntag public function commonGivenTable(int $threshold = 1, int $maxtoshow = 10): string 2308add1155SRico Sonntag { 2318add1155SRico Sonntag return $this->commonGivenQuery('B', 'table', false, $threshold, $maxtoshow); 2328add1155SRico Sonntag } 2338add1155SRico Sonntag 2348add1155SRico Sonntag /** 2358add1155SRico Sonntag * Find common give names of females. 2368add1155SRico Sonntag * 2378add1155SRico Sonntag * @param int $threshold 2388add1155SRico Sonntag * @param int $maxtoshow 2398add1155SRico Sonntag * 2408add1155SRico Sonntag * @return string 2418add1155SRico Sonntag */ 2428add1155SRico Sonntag public function commonGivenFemale(int $threshold = 1, int $maxtoshow = 10): string 2438add1155SRico Sonntag { 2448add1155SRico Sonntag return $this->commonGivenQuery('F', 'nolist', false, $threshold, $maxtoshow); 2458add1155SRico Sonntag } 2468add1155SRico Sonntag 2478add1155SRico Sonntag /** 2488add1155SRico Sonntag * Find common give names of females. 2498add1155SRico Sonntag * 2508add1155SRico Sonntag * @param int $threshold 2518add1155SRico Sonntag * @param int $maxtoshow 2528add1155SRico Sonntag * 2538add1155SRico Sonntag * @return string 2548add1155SRico Sonntag */ 2558add1155SRico Sonntag public function commonGivenFemaleTotals(int $threshold = 1, int $maxtoshow = 10): string 2568add1155SRico Sonntag { 2578add1155SRico Sonntag return $this->commonGivenQuery('F', 'nolist', true, $threshold, $maxtoshow); 2588add1155SRico Sonntag } 2598add1155SRico Sonntag 2608add1155SRico Sonntag /** 2618add1155SRico Sonntag * Find common give names of females. 2628add1155SRico Sonntag * 2638add1155SRico Sonntag * @param int $threshold 2648add1155SRico Sonntag * @param int $maxtoshow 2658add1155SRico Sonntag * 2668add1155SRico Sonntag * @return string 2678add1155SRico Sonntag */ 2688add1155SRico Sonntag public function commonGivenFemaleList(int $threshold = 1, int $maxtoshow = 10): string 2698add1155SRico Sonntag { 2708add1155SRico Sonntag return $this->commonGivenQuery('F', 'list', false, $threshold, $maxtoshow); 2718add1155SRico Sonntag } 2728add1155SRico Sonntag 2738add1155SRico Sonntag /** 2748add1155SRico Sonntag * Find common give names of females. 2758add1155SRico Sonntag * 2768add1155SRico Sonntag * @param int $threshold 2778add1155SRico Sonntag * @param int $maxtoshow 2788add1155SRico Sonntag * 2798add1155SRico Sonntag * @return string 2808add1155SRico Sonntag */ 2818add1155SRico Sonntag public function commonGivenFemaleListTotals(int $threshold = 1, int $maxtoshow = 10): string 2828add1155SRico Sonntag { 2838add1155SRico Sonntag return $this->commonGivenQuery('F', 'list', true, $threshold, $maxtoshow); 2848add1155SRico Sonntag } 2858add1155SRico Sonntag 2868add1155SRico Sonntag /** 2878add1155SRico Sonntag * Find common give names of females. 2888add1155SRico Sonntag * 2898add1155SRico Sonntag * @param int $threshold 2908add1155SRico Sonntag * @param int $maxtoshow 2918add1155SRico Sonntag * 2928add1155SRico Sonntag * @return string 2938add1155SRico Sonntag */ 2948add1155SRico Sonntag public function commonGivenFemaleTable(int $threshold = 1, int $maxtoshow = 10): string 2958add1155SRico Sonntag { 2968add1155SRico Sonntag return $this->commonGivenQuery('F', 'table', false, $threshold, $maxtoshow); 2978add1155SRico Sonntag } 2988add1155SRico Sonntag 2998add1155SRico Sonntag /** 3008add1155SRico Sonntag * Find common give names of males. 3018add1155SRico Sonntag * 3028add1155SRico Sonntag * @param int $threshold 3038add1155SRico Sonntag * @param int $maxtoshow 3048add1155SRico Sonntag * 3058add1155SRico Sonntag * @return string 3068add1155SRico Sonntag */ 3078add1155SRico Sonntag public function commonGivenMale(int $threshold = 1, int $maxtoshow = 10): string 3088add1155SRico Sonntag { 3098add1155SRico Sonntag return $this->commonGivenQuery('M', 'nolist', false, $threshold, $maxtoshow); 3108add1155SRico Sonntag } 3118add1155SRico Sonntag 3128add1155SRico Sonntag /** 3138add1155SRico Sonntag * Find common give names of males. 3148add1155SRico Sonntag * 3158add1155SRico Sonntag * @param int $threshold 3168add1155SRico Sonntag * @param int $maxtoshow 3178add1155SRico Sonntag * 3188add1155SRico Sonntag * @return string 3198add1155SRico Sonntag */ 3208add1155SRico Sonntag public function commonGivenMaleTotals(int $threshold = 1, int $maxtoshow = 10): string 3218add1155SRico Sonntag { 3228add1155SRico Sonntag return $this->commonGivenQuery('M', 'nolist', true, $threshold, $maxtoshow); 3238add1155SRico Sonntag } 3248add1155SRico Sonntag 3258add1155SRico Sonntag /** 3268add1155SRico Sonntag * Find common give names of males. 3278add1155SRico Sonntag * 3288add1155SRico Sonntag * @param int $threshold 3298add1155SRico Sonntag * @param int $maxtoshow 3308add1155SRico Sonntag * 3318add1155SRico Sonntag * @return string 3328add1155SRico Sonntag */ 3338add1155SRico Sonntag public function commonGivenMaleList(int $threshold = 1, int $maxtoshow = 10): string 3348add1155SRico Sonntag { 3358add1155SRico Sonntag return $this->commonGivenQuery('M', 'list', false, $threshold, $maxtoshow); 3368add1155SRico Sonntag } 3378add1155SRico Sonntag 3388add1155SRico Sonntag /** 3398add1155SRico Sonntag * Find common give names of males. 3408add1155SRico Sonntag * 3418add1155SRico Sonntag * @param int $threshold 3428add1155SRico Sonntag * @param int $maxtoshow 3438add1155SRico Sonntag * 3448add1155SRico Sonntag * @return string 3458add1155SRico Sonntag */ 3468add1155SRico Sonntag public function commonGivenMaleListTotals(int $threshold = 1, int $maxtoshow = 10): string 3478add1155SRico Sonntag { 3488add1155SRico Sonntag return $this->commonGivenQuery('M', 'list', true, $threshold, $maxtoshow); 3498add1155SRico Sonntag } 3508add1155SRico Sonntag 3518add1155SRico Sonntag /** 3528add1155SRico Sonntag * Find common give names of males. 3538add1155SRico Sonntag * 3548add1155SRico Sonntag * @param int $threshold 3558add1155SRico Sonntag * @param int $maxtoshow 3568add1155SRico Sonntag * 3578add1155SRico Sonntag * @return string 3588add1155SRico Sonntag */ 3598add1155SRico Sonntag public function commonGivenMaleTable(int $threshold = 1, int $maxtoshow = 10): string 3608add1155SRico Sonntag { 3618add1155SRico Sonntag return $this->commonGivenQuery('M', 'table', false, $threshold, $maxtoshow); 3628add1155SRico Sonntag } 3638add1155SRico Sonntag 3648add1155SRico Sonntag /** 3658add1155SRico Sonntag * Find common give names of unknown sexes. 3668add1155SRico Sonntag * 3678add1155SRico Sonntag * @param int $threshold 3688add1155SRico Sonntag * @param int $maxtoshow 3698add1155SRico Sonntag * 3708add1155SRico Sonntag * @return string 3718add1155SRico Sonntag */ 3728add1155SRico Sonntag public function commonGivenUnknown(int $threshold = 1, int $maxtoshow = 10): string 3738add1155SRico Sonntag { 3748add1155SRico Sonntag return $this->commonGivenQuery('U', 'nolist', false, $threshold, $maxtoshow); 3758add1155SRico Sonntag } 3768add1155SRico Sonntag 3778add1155SRico Sonntag /** 3788add1155SRico Sonntag * Find common give names of unknown sexes. 3798add1155SRico Sonntag * 3808add1155SRico Sonntag * @param int $threshold 3818add1155SRico Sonntag * @param int $maxtoshow 3828add1155SRico Sonntag * 3838add1155SRico Sonntag * @return string 3848add1155SRico Sonntag */ 3858add1155SRico Sonntag public function commonGivenUnknownTotals(int $threshold = 1, int $maxtoshow = 10): string 3868add1155SRico Sonntag { 3878add1155SRico Sonntag return $this->commonGivenQuery('U', 'nolist', true, $threshold, $maxtoshow); 3888add1155SRico Sonntag } 3898add1155SRico Sonntag 3908add1155SRico Sonntag /** 3918add1155SRico Sonntag * Find common give names of unknown sexes. 3928add1155SRico Sonntag * 3938add1155SRico Sonntag * @param int $threshold 3948add1155SRico Sonntag * @param int $maxtoshow 3958add1155SRico Sonntag * 3968add1155SRico Sonntag * @return string 3978add1155SRico Sonntag */ 3988add1155SRico Sonntag public function commonGivenUnknownList(int $threshold = 1, int $maxtoshow = 10): string 3998add1155SRico Sonntag { 4008add1155SRico Sonntag return $this->commonGivenQuery('U', 'list', false, $threshold, $maxtoshow); 4018add1155SRico Sonntag } 4028add1155SRico Sonntag 4038add1155SRico Sonntag /** 4048add1155SRico Sonntag * Find common give names of unknown sexes. 4058add1155SRico Sonntag * 4068add1155SRico Sonntag * @param int $threshold 4078add1155SRico Sonntag * @param int $maxtoshow 4088add1155SRico Sonntag * 4098add1155SRico Sonntag * @return string 4108add1155SRico Sonntag */ 4118add1155SRico Sonntag public function commonGivenUnknownListTotals(int $threshold = 1, int $maxtoshow = 10): string 4128add1155SRico Sonntag { 4138add1155SRico Sonntag return $this->commonGivenQuery('U', 'list', true, $threshold, $maxtoshow); 4148add1155SRico Sonntag } 4158add1155SRico Sonntag 4168add1155SRico Sonntag /** 4178add1155SRico Sonntag * Find common give names of unknown sexes. 4188add1155SRico Sonntag * 4198add1155SRico Sonntag * @param int $threshold 4208add1155SRico Sonntag * @param int $maxtoshow 4218add1155SRico Sonntag * 4228add1155SRico Sonntag * @return string 4238add1155SRico Sonntag */ 4248add1155SRico Sonntag public function commonGivenUnknownTable(int $threshold = 1, int $maxtoshow = 10): string 4258add1155SRico Sonntag { 4268add1155SRico Sonntag return $this->commonGivenQuery('U', 'table', false, $threshold, $maxtoshow); 4278add1155SRico Sonntag } 4288add1155SRico Sonntag 4298add1155SRico Sonntag /** 4303dc8167dSGreg Roach * Count the number of distinct given names (or the number of occurences of specific given names). 4318add1155SRico Sonntag * 432*09482a55SGreg Roach * @param array<string> ...$params 4338add1155SRico Sonntag * 4348add1155SRico Sonntag * @return string 4358add1155SRico Sonntag */ 4368add1155SRico Sonntag public function totalGivennames(...$params): string 4378add1155SRico Sonntag { 4383dc8167dSGreg Roach $query = DB::table('name') 4393dc8167dSGreg Roach ->where('n_file', '=', $this->tree->id()); 4403dc8167dSGreg Roach 441320f6a24SGreg Roach if ($params === []) { 442c09e99bfSRico Sonntag // Count number of distinct given names. 4433dc8167dSGreg Roach $query 444c09e99bfSRico Sonntag ->distinct() 4458fb4e87cSGreg Roach ->where('n_givn', '<>', Individual::PRAENOMEN_NESCIO) 446c09e99bfSRico Sonntag ->whereNotNull('n_givn'); 4478add1155SRico Sonntag } else { 448c09e99bfSRico Sonntag // Count number of occurences of specific given names. 44944cdc21eSGreg Roach $query->whereIn('n_givn', $params); 4508add1155SRico Sonntag } 4518add1155SRico Sonntag 452c09e99bfSRico Sonntag $count = $query->count('n_givn'); 4533dc8167dSGreg Roach 4543dc8167dSGreg Roach return I18N::number($count); 4558add1155SRico Sonntag } 4568add1155SRico Sonntag 4578add1155SRico Sonntag /** 458320f6a24SGreg Roach * Count the number of distinct surnames (or the number of occurrences of specific surnames). 4598add1155SRico Sonntag * 460*09482a55SGreg Roach * @param array<string> ...$params 4618add1155SRico Sonntag * 4628add1155SRico Sonntag * @return string 4638add1155SRico Sonntag */ 4648add1155SRico Sonntag public function totalSurnames(...$params): string 4658add1155SRico Sonntag { 4663dc8167dSGreg Roach $query = DB::table('name') 4673dc8167dSGreg Roach ->where('n_file', '=', $this->tree->id()); 4683dc8167dSGreg Roach 469320f6a24SGreg Roach if ($params === []) { 4703dc8167dSGreg Roach // Count number of distinct surnames 471c09e99bfSRico Sonntag $query->distinct() 472c09e99bfSRico Sonntag ->whereNotNull('n_surn'); 4738add1155SRico Sonntag } else { 4743dc8167dSGreg Roach // Count number of occurences of specific surnames. 4753dc8167dSGreg Roach $query->whereIn('n_surn', $params); 4768add1155SRico Sonntag } 4778add1155SRico Sonntag 478c09e99bfSRico Sonntag $count = $query->count('n_surn'); 4798add1155SRico Sonntag 4803dc8167dSGreg Roach return I18N::number($count); 4818add1155SRico Sonntag } 4828add1155SRico Sonntag 4838add1155SRico Sonntag /** 4848add1155SRico Sonntag * @param int $number_of_surnames 4858add1155SRico Sonntag * @param int $threshold 4868add1155SRico Sonntag * 487*09482a55SGreg Roach * @return array<array<int>> 4888add1155SRico Sonntag */ 4898add1155SRico Sonntag private function topSurnames(int $number_of_surnames, int $threshold): array 4908add1155SRico Sonntag { 4918add1155SRico Sonntag // Use the count of base surnames. 492d1a467e4SGreg Roach $top_surnames = DB::table('name') 493d1a467e4SGreg Roach ->where('n_file', '=', $this->tree->id()) 494d1a467e4SGreg Roach ->where('n_type', '<>', '_MARNM') 4958fb4e87cSGreg Roach ->whereNotIn('n_surn', ['', Individual::NOMEN_NESCIO]) 49647256fc5SGreg Roach ->select(['n_surn']) 4977f5c2944SGreg Roach ->groupBy(['n_surn']) 4982da2e0a6SGreg Roach ->orderByRaw('COUNT(n_surn) DESC') 4992da2e0a6SGreg Roach ->orderBy(new Expression('COUNT(n_surn)'), 'DESC') 5002da2e0a6SGreg Roach ->having(new Expression('COUNT(n_surn)'), '>=', $threshold) 501d1a467e4SGreg Roach ->take($number_of_surnames) 502d1a467e4SGreg Roach ->get() 503d1a467e4SGreg Roach ->pluck('n_surn') 504d1a467e4SGreg Roach ->all(); 5058add1155SRico Sonntag 5068add1155SRico Sonntag $surnames = []; 5072da2e0a6SGreg Roach 5088add1155SRico Sonntag foreach ($top_surnames as $top_surname) { 5092da2e0a6SGreg Roach $surnames[$top_surname] = DB::table('name') 510d1a467e4SGreg Roach ->where('n_file', '=', $this->tree->id()) 5112da2e0a6SGreg Roach ->where('n_type', '<>', '_MARNM') 5122da2e0a6SGreg Roach ->where('n_surn', '=', $top_surname) 5132da2e0a6SGreg Roach ->select(['n_surn', new Expression('COUNT(n_surn) AS count')]) 5147f5c2944SGreg Roach ->groupBy(['n_surn']) 5152da2e0a6SGreg Roach ->orderBy('n_surn') 516d1a467e4SGreg Roach ->get() 517d1a467e4SGreg Roach ->pluck('count', 'n_surn') 518d1a467e4SGreg Roach ->all(); 5198add1155SRico Sonntag } 5208add1155SRico Sonntag 5218add1155SRico Sonntag return $surnames; 5228add1155SRico Sonntag } 5238add1155SRico Sonntag 5248add1155SRico Sonntag /** 5258add1155SRico Sonntag * Find common surnames. 5268add1155SRico Sonntag * 5278add1155SRico Sonntag * @return string 5288add1155SRico Sonntag */ 5298add1155SRico Sonntag public function getCommonSurname(): string 5308add1155SRico Sonntag { 5318add1155SRico Sonntag $top_surname = $this->topSurnames(1, 0); 532f24db0ceSRico Sonntag 533f24db0ceSRico Sonntag return $top_surname 534f24db0ceSRico Sonntag ? implode(', ', array_keys(array_shift($top_surname)) ?? []) 535f24db0ceSRico Sonntag : ''; 5368add1155SRico Sonntag } 5378add1155SRico Sonntag 5388add1155SRico Sonntag /** 5398add1155SRico Sonntag * Find common surnames. 5408add1155SRico Sonntag * 5418add1155SRico Sonntag * @param string $type 5428add1155SRico Sonntag * @param bool $show_tot 5438add1155SRico Sonntag * @param int $threshold 5448add1155SRico Sonntag * @param int $number_of_surnames 5458add1155SRico Sonntag * @param string $sorting 5468add1155SRico Sonntag * 5478add1155SRico Sonntag * @return string 5488add1155SRico Sonntag */ 5498add1155SRico Sonntag private function commonSurnamesQuery( 5508add1155SRico Sonntag string $type, 5518add1155SRico Sonntag bool $show_tot, 5528add1155SRico Sonntag int $threshold, 5538add1155SRico Sonntag int $number_of_surnames, 5548add1155SRico Sonntag string $sorting 5558add1155SRico Sonntag ): string { 5568add1155SRico Sonntag $surnames = $this->topSurnames($number_of_surnames, $threshold); 5578add1155SRico Sonntag 5588add1155SRico Sonntag switch ($sorting) { 5598add1155SRico Sonntag default: 5608add1155SRico Sonntag case 'alpha': 56137646143SGreg Roach uksort($surnames, I18N::comparator()); 5628add1155SRico Sonntag break; 5638add1155SRico Sonntag case 'count': 5648add1155SRico Sonntag break; 5658add1155SRico Sonntag case 'rcount': 5668add1155SRico Sonntag $surnames = array_reverse($surnames, true); 5678add1155SRico Sonntag break; 5688add1155SRico Sonntag } 5698add1155SRico Sonntag 57067992b6aSRichard Cissee //find a module providing individual lists 5710b5fd0a6SGreg Roach $module = app(ModuleService::class)->findByComponent(ModuleListInterface::class, $this->tree, Auth::user())->first(static function (ModuleInterface $module): bool { 57267992b6aSRichard Cissee return $module instanceof IndividualListModule; 57367992b6aSRichard Cissee }); 57467992b6aSRichard Cissee 5758add1155SRico Sonntag return FunctionsPrintLists::surnameList( 5768add1155SRico Sonntag $surnames, 5778add1155SRico Sonntag ($type === 'list' ? 1 : 2), 5788add1155SRico Sonntag $show_tot, 57967992b6aSRichard Cissee $module, 5808add1155SRico Sonntag $this->tree 5818add1155SRico Sonntag ); 5828add1155SRico Sonntag } 5838add1155SRico Sonntag 5848add1155SRico Sonntag /** 5858add1155SRico Sonntag * Find common surnames. 5868add1155SRico Sonntag * 5878add1155SRico Sonntag * @param int $threshold 5888add1155SRico Sonntag * @param int $number_of_surnames 5898add1155SRico Sonntag * @param string $sorting 5908add1155SRico Sonntag * 5918add1155SRico Sonntag * @return string 5928add1155SRico Sonntag */ 5938add1155SRico Sonntag public function commonSurnames( 5948add1155SRico Sonntag int $threshold = 1, 5958add1155SRico Sonntag int $number_of_surnames = 10, 5968add1155SRico Sonntag string $sorting = 'alpha' 5978add1155SRico Sonntag ): string { 5988add1155SRico Sonntag return $this->commonSurnamesQuery('nolist', false, $threshold, $number_of_surnames, $sorting); 5998add1155SRico Sonntag } 6008add1155SRico Sonntag 6018add1155SRico Sonntag /** 6028add1155SRico Sonntag * Find common surnames. 6038add1155SRico Sonntag * 6048add1155SRico Sonntag * @param int $threshold 6058add1155SRico Sonntag * @param int $number_of_surnames 6068add1155SRico Sonntag * @param string $sorting 6078add1155SRico Sonntag * 6088add1155SRico Sonntag * @return string 6098add1155SRico Sonntag */ 6108add1155SRico Sonntag public function commonSurnamesTotals( 6118add1155SRico Sonntag int $threshold = 1, 6128add1155SRico Sonntag int $number_of_surnames = 10, 6132da2e0a6SGreg Roach string $sorting = 'count' 6148add1155SRico Sonntag ): string { 6158add1155SRico Sonntag return $this->commonSurnamesQuery('nolist', true, $threshold, $number_of_surnames, $sorting); 6168add1155SRico Sonntag } 6178add1155SRico Sonntag 6188add1155SRico Sonntag /** 6198add1155SRico Sonntag * Find common surnames. 6208add1155SRico Sonntag * 6218add1155SRico Sonntag * @param int $threshold 6228add1155SRico Sonntag * @param int $number_of_surnames 6238add1155SRico Sonntag * @param string $sorting 6248add1155SRico Sonntag * 6258add1155SRico Sonntag * @return string 6268add1155SRico Sonntag */ 6278add1155SRico Sonntag public function commonSurnamesList( 6288add1155SRico Sonntag int $threshold = 1, 6298add1155SRico Sonntag int $number_of_surnames = 10, 6308add1155SRico Sonntag string $sorting = 'alpha' 6318add1155SRico Sonntag ): string { 6328add1155SRico Sonntag return $this->commonSurnamesQuery('list', false, $threshold, $number_of_surnames, $sorting); 6338add1155SRico Sonntag } 6348add1155SRico Sonntag 6358add1155SRico Sonntag /** 6368add1155SRico Sonntag * Find common surnames. 6378add1155SRico Sonntag * 6388add1155SRico Sonntag * @param int $threshold 6398add1155SRico Sonntag * @param int $number_of_surnames 6408add1155SRico Sonntag * @param string $sorting 6418add1155SRico Sonntag * 6428add1155SRico Sonntag * @return string 6438add1155SRico Sonntag */ 6448add1155SRico Sonntag public function commonSurnamesListTotals( 6458add1155SRico Sonntag int $threshold = 1, 6468add1155SRico Sonntag int $number_of_surnames = 10, 6472da2e0a6SGreg Roach string $sorting = 'count' 6488add1155SRico Sonntag ): string { 6498add1155SRico Sonntag return $this->commonSurnamesQuery('list', true, $threshold, $number_of_surnames, $sorting); 6508add1155SRico Sonntag } 6518add1155SRico Sonntag 6528add1155SRico Sonntag /** 653cde1d378SGreg Roach * Get a count of births by month. 6548add1155SRico Sonntag * 6558add1155SRico Sonntag * @param int $year1 6568add1155SRico Sonntag * @param int $year2 6578add1155SRico Sonntag * 658cde1d378SGreg Roach * @return Builder 6598add1155SRico Sonntag */ 660cde1d378SGreg Roach public function statsBirthQuery(int $year1 = -1, int $year2 = -1): Builder 6618add1155SRico Sonntag { 662d1a467e4SGreg Roach $query = DB::table('dates') 663a69f5655SGreg Roach ->select(['d_month', new Expression('COUNT(*) AS total')]) 664d1a467e4SGreg Roach ->where('d_file', '=', $this->tree->id()) 665d1a467e4SGreg Roach ->where('d_fact', '=', 'BIRT') 666d1a467e4SGreg Roach ->whereIn('d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 6677f5c2944SGreg Roach ->groupBy(['d_month']); 6688add1155SRico Sonntag 6698add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 670d1a467e4SGreg Roach $query->whereBetween('d_year', [$year1, $year2]); 6718add1155SRico Sonntag } 6728add1155SRico Sonntag 673cde1d378SGreg Roach return $query; 674cde1d378SGreg Roach } 675cde1d378SGreg Roach 676cde1d378SGreg Roach /** 677cde1d378SGreg Roach * Get a count of births by month. 678cde1d378SGreg Roach * 679cde1d378SGreg Roach * @param int $year1 680cde1d378SGreg Roach * @param int $year2 681cde1d378SGreg Roach * 682cde1d378SGreg Roach * @return Builder 683cde1d378SGreg Roach */ 684cde1d378SGreg Roach public function statsBirthBySexQuery(int $year1 = -1, int $year2 = -1): Builder 685cde1d378SGreg Roach { 686cde1d378SGreg Roach return $this->statsBirthQuery($year1, $year2) 687a69f5655SGreg Roach ->select(['d_month', 'i_sex', new Expression('COUNT(*) AS total')]) 6880b5fd0a6SGreg Roach ->join('individuals', static function (JoinClause $join): void { 689d1a467e4SGreg Roach $join 690d1a467e4SGreg Roach ->on('i_id', '=', 'd_gid') 691d1a467e4SGreg Roach ->on('i_file', '=', 'd_file'); 692d1a467e4SGreg Roach }) 6937f5c2944SGreg Roach ->groupBy(['i_sex']); 6948add1155SRico Sonntag } 6958add1155SRico Sonntag 6968add1155SRico Sonntag /** 6978add1155SRico Sonntag * General query on births. 6988add1155SRico Sonntag * 6998add1155SRico Sonntag * @param string|null $color_from 7008add1155SRico Sonntag * @param string|null $color_to 7018add1155SRico Sonntag * 7028add1155SRico Sonntag * @return string 7038add1155SRico Sonntag */ 70488de55fdSRico Sonntag public function statsBirth(string $color_from = null, string $color_to = null): string 7058add1155SRico Sonntag { 7068add1155SRico Sonntag return (new ChartBirth($this->tree)) 70788de55fdSRico Sonntag ->chartBirth($color_from, $color_to); 7088add1155SRico Sonntag } 7098add1155SRico Sonntag 7108add1155SRico Sonntag /** 7118add1155SRico Sonntag * Get a list of death dates. 7128add1155SRico Sonntag * 7138add1155SRico Sonntag * @param int $year1 7148add1155SRico Sonntag * @param int $year2 7158add1155SRico Sonntag * 716cde1d378SGreg Roach * @return Builder 7178add1155SRico Sonntag */ 718cde1d378SGreg Roach public function statsDeathQuery(int $year1 = -1, int $year2 = -1): Builder 7198add1155SRico Sonntag { 720d1a467e4SGreg Roach $query = DB::table('dates') 721a69f5655SGreg Roach ->select(['d_month', new Expression('COUNT(*) AS total')]) 722d1a467e4SGreg Roach ->where('d_file', '=', $this->tree->id()) 723d1a467e4SGreg Roach ->where('d_fact', '=', 'DEAT') 724d1a467e4SGreg Roach ->whereIn('d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 7257f5c2944SGreg Roach ->groupBy(['d_month']); 7268add1155SRico Sonntag 7278add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 728d1a467e4SGreg Roach $query->whereBetween('d_year', [$year1, $year2]); 7298add1155SRico Sonntag } 7308add1155SRico Sonntag 731cde1d378SGreg Roach return $query; 732cde1d378SGreg Roach } 733cde1d378SGreg Roach 734cde1d378SGreg Roach /** 735cde1d378SGreg Roach * Get a list of death dates. 736cde1d378SGreg Roach * 737cde1d378SGreg Roach * @param int $year1 738cde1d378SGreg Roach * @param int $year2 739cde1d378SGreg Roach * 740cde1d378SGreg Roach * @return Builder 741cde1d378SGreg Roach */ 742cde1d378SGreg Roach public function statsDeathBySexQuery(int $year1 = -1, int $year2 = -1): Builder 743cde1d378SGreg Roach { 744cde1d378SGreg Roach return $this->statsDeathQuery($year1, $year2) 745a69f5655SGreg Roach ->select(['d_month', 'i_sex', new Expression('COUNT(*) AS total')]) 7460b5fd0a6SGreg Roach ->join('individuals', static function (JoinClause $join): void { 747d1a467e4SGreg Roach $join 748d1a467e4SGreg Roach ->on('i_id', '=', 'd_gid') 749d1a467e4SGreg Roach ->on('i_file', '=', 'd_file'); 750d1a467e4SGreg Roach }) 7517f5c2944SGreg Roach ->groupBy(['i_sex']); 7528add1155SRico Sonntag } 7538add1155SRico Sonntag 7548add1155SRico Sonntag /** 7558add1155SRico Sonntag * General query on deaths. 7568add1155SRico Sonntag * 7578add1155SRico Sonntag * @param string|null $color_from 7588add1155SRico Sonntag * @param string|null $color_to 7598add1155SRico Sonntag * 7608add1155SRico Sonntag * @return string 7618add1155SRico Sonntag */ 76288de55fdSRico Sonntag public function statsDeath(string $color_from = null, string $color_to = null): string 7638add1155SRico Sonntag { 7648add1155SRico Sonntag return (new ChartDeath($this->tree)) 76588de55fdSRico Sonntag ->chartDeath($color_from, $color_to); 7668add1155SRico Sonntag } 7678add1155SRico Sonntag 7688add1155SRico Sonntag /** 7698add1155SRico Sonntag * General query on ages. 7708add1155SRico Sonntag * 7718add1155SRico Sonntag * @param string $related 7728add1155SRico Sonntag * @param string $sex 7738add1155SRico Sonntag * @param int $year1 7748add1155SRico Sonntag * @param int $year2 7758add1155SRico Sonntag * 77691c84b80SGreg Roach * @return array<stdClass> 7778add1155SRico Sonntag */ 778d823340dSGreg Roach public function statsAgeQuery(string $related = 'BIRT', string $sex = 'BOTH', int $year1 = -1, int $year2 = -1): array 7798add1155SRico Sonntag { 78044cdc21eSGreg Roach $prefix = DB::connection()->getTablePrefix(); 7818add1155SRico Sonntag 78244cdc21eSGreg Roach $query = $this->birthAndDeathQuery($sex); 7838add1155SRico Sonntag 7848add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 78544cdc21eSGreg Roach $query 78644cdc21eSGreg Roach ->whereIn('birth.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 78744cdc21eSGreg Roach ->whereIn('death.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']); 78844cdc21eSGreg Roach 7898add1155SRico Sonntag if ($related === 'BIRT') { 79044cdc21eSGreg Roach $query->whereBetween('birth.d_year', [$year1, $year2]); 7918add1155SRico Sonntag } elseif ($related === 'DEAT') { 79244cdc21eSGreg Roach $query->whereBetween('death.d_year', [$year1, $year2]); 7938add1155SRico Sonntag } 7948add1155SRico Sonntag } 7958add1155SRico Sonntag 79644cdc21eSGreg Roach return $query 797a69f5655SGreg Roach ->select(new Expression($prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1 AS days')) 798c8c87812SRico Sonntag ->orderBy('days', 'desc') 79944cdc21eSGreg Roach ->get() 80044cdc21eSGreg Roach ->all(); 8018add1155SRico Sonntag } 8028add1155SRico Sonntag 8038add1155SRico Sonntag /** 8048add1155SRico Sonntag * General query on ages. 8058add1155SRico Sonntag * 8068add1155SRico Sonntag * @return string 8078add1155SRico Sonntag */ 80888de55fdSRico Sonntag public function statsAge(): string 8098add1155SRico Sonntag { 81088de55fdSRico Sonntag return (new ChartAge($this->tree))->chartAge(); 8118add1155SRico Sonntag } 8128add1155SRico Sonntag 8138add1155SRico Sonntag /** 8148add1155SRico Sonntag * Lifespan 8158add1155SRico Sonntag * 8168add1155SRico Sonntag * @param string $type 8178add1155SRico Sonntag * @param string $sex 8188add1155SRico Sonntag * 8198add1155SRico Sonntag * @return string 8208add1155SRico Sonntag */ 8218add1155SRico Sonntag private function longlifeQuery(string $type, string $sex): string 8228add1155SRico Sonntag { 82344cdc21eSGreg Roach $prefix = DB::connection()->getTablePrefix(); 8248add1155SRico Sonntag 82544cdc21eSGreg Roach $row = $this->birthAndDeathQuery($sex) 82644cdc21eSGreg Roach ->orderBy('days', 'desc') 827a69f5655SGreg Roach ->select(['individuals.*', new Expression($prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1 AS days')]) 82844cdc21eSGreg Roach ->first(); 82944cdc21eSGreg Roach 83044cdc21eSGreg Roach if ($row === null) { 8318add1155SRico Sonntag return ''; 8328add1155SRico Sonntag } 83344cdc21eSGreg Roach 83444cdc21eSGreg Roach /** @var Individual $individual */ 8356b9cb339SGreg Roach $individual = Registry::individualFactory()->mapper($this->tree)($row); 83644cdc21eSGreg Roach 837747d54c2SGreg Roach if ($type !== 'age' && !$individual->canShow()) { 83844cdc21eSGreg Roach return I18N::translate('This information is private and cannot be shown.'); 83944cdc21eSGreg Roach } 84044cdc21eSGreg Roach 8418add1155SRico Sonntag switch ($type) { 8428add1155SRico Sonntag default: 8438add1155SRico Sonntag case 'full': 84444cdc21eSGreg Roach return $individual->formatList(); 8458add1155SRico Sonntag 84644cdc21eSGreg Roach case 'age': 84744cdc21eSGreg Roach return I18N::number((int) ($row->days / 365.25)); 84844cdc21eSGreg Roach 84944cdc21eSGreg Roach case 'name': 85039ca88baSGreg Roach return '<a href="' . e($individual->url()) . '">' . $individual->fullName() . '</a>'; 85144cdc21eSGreg Roach } 8528add1155SRico Sonntag } 8538add1155SRico Sonntag 8548add1155SRico Sonntag /** 8558add1155SRico Sonntag * Find the longest lived individual. 8568add1155SRico Sonntag * 8578add1155SRico Sonntag * @return string 8588add1155SRico Sonntag */ 8598add1155SRico Sonntag public function longestLife(): string 8608add1155SRico Sonntag { 8618add1155SRico Sonntag return $this->longlifeQuery('full', 'BOTH'); 8628add1155SRico Sonntag } 8638add1155SRico Sonntag 8648add1155SRico Sonntag /** 8658add1155SRico Sonntag * Find the age of the longest lived individual. 8668add1155SRico Sonntag * 8678add1155SRico Sonntag * @return string 8688add1155SRico Sonntag */ 8698add1155SRico Sonntag public function longestLifeAge(): string 8708add1155SRico Sonntag { 8718add1155SRico Sonntag return $this->longlifeQuery('age', 'BOTH'); 8728add1155SRico Sonntag } 8738add1155SRico Sonntag 8748add1155SRico Sonntag /** 8758add1155SRico Sonntag * Find the name of the longest lived individual. 8768add1155SRico Sonntag * 8778add1155SRico Sonntag * @return string 8788add1155SRico Sonntag */ 8798add1155SRico Sonntag public function longestLifeName(): string 8808add1155SRico Sonntag { 8818add1155SRico Sonntag return $this->longlifeQuery('name', 'BOTH'); 8828add1155SRico Sonntag } 8838add1155SRico Sonntag 8848add1155SRico Sonntag /** 8858add1155SRico Sonntag * Find the longest lived female. 8868add1155SRico Sonntag * 8878add1155SRico Sonntag * @return string 8888add1155SRico Sonntag */ 8898add1155SRico Sonntag public function longestLifeFemale(): string 8908add1155SRico Sonntag { 8918add1155SRico Sonntag return $this->longlifeQuery('full', 'F'); 8928add1155SRico Sonntag } 8938add1155SRico Sonntag 8948add1155SRico Sonntag /** 8958add1155SRico Sonntag * Find the age of the longest lived female. 8968add1155SRico Sonntag * 8978add1155SRico Sonntag * @return string 8988add1155SRico Sonntag */ 8998add1155SRico Sonntag public function longestLifeFemaleAge(): string 9008add1155SRico Sonntag { 9018add1155SRico Sonntag return $this->longlifeQuery('age', 'F'); 9028add1155SRico Sonntag } 9038add1155SRico Sonntag 9048add1155SRico Sonntag /** 9058add1155SRico Sonntag * Find the name of the longest lived female. 9068add1155SRico Sonntag * 9078add1155SRico Sonntag * @return string 9088add1155SRico Sonntag */ 9098add1155SRico Sonntag public function longestLifeFemaleName(): string 9108add1155SRico Sonntag { 9118add1155SRico Sonntag return $this->longlifeQuery('name', 'F'); 9128add1155SRico Sonntag } 9138add1155SRico Sonntag 9148add1155SRico Sonntag /** 9158add1155SRico Sonntag * Find the longest lived male. 9168add1155SRico Sonntag * 9178add1155SRico Sonntag * @return string 9188add1155SRico Sonntag */ 9198add1155SRico Sonntag public function longestLifeMale(): string 9208add1155SRico Sonntag { 9218add1155SRico Sonntag return $this->longlifeQuery('full', 'M'); 9228add1155SRico Sonntag } 9238add1155SRico Sonntag 9248add1155SRico Sonntag /** 9258add1155SRico Sonntag * Find the age of the longest lived male. 9268add1155SRico Sonntag * 9278add1155SRico Sonntag * @return string 9288add1155SRico Sonntag */ 9298add1155SRico Sonntag public function longestLifeMaleAge(): string 9308add1155SRico Sonntag { 9318add1155SRico Sonntag return $this->longlifeQuery('age', 'M'); 9328add1155SRico Sonntag } 9338add1155SRico Sonntag 9348add1155SRico Sonntag /** 9358add1155SRico Sonntag * Find the name of the longest lived male. 9368add1155SRico Sonntag * 9378add1155SRico Sonntag * @return string 9388add1155SRico Sonntag */ 9398add1155SRico Sonntag public function longestLifeMaleName(): string 9408add1155SRico Sonntag { 9418add1155SRico Sonntag return $this->longlifeQuery('name', 'M'); 9428add1155SRico Sonntag } 9438add1155SRico Sonntag 9448add1155SRico Sonntag /** 9458add1155SRico Sonntag * Returns the calculated age the time of event. 9468add1155SRico Sonntag * 947054771e9SGreg Roach * @param int $days The age from the database record 9488add1155SRico Sonntag * 9498add1155SRico Sonntag * @return string 9508add1155SRico Sonntag */ 951054771e9SGreg Roach private function calculateAge(int $days): string 9528add1155SRico Sonntag { 953054771e9SGreg Roach if ($days < 31) { 954054771e9SGreg Roach return I18N::plural('%s day', '%s days', $days, I18N::number($days)); 9558add1155SRico Sonntag } 9568add1155SRico Sonntag 957054771e9SGreg Roach if ($days < 365) { 958054771e9SGreg Roach $months = (int) ($days / 30.5); 9591061e22bSGreg Roach return I18N::plural('%s month', '%s months', $months, I18N::number($months)); 960054771e9SGreg Roach } 961054771e9SGreg Roach 962054771e9SGreg Roach $years = (int) ($days / 365.25); 963054771e9SGreg Roach 964054771e9SGreg Roach return I18N::plural('%s year', '%s years', $years, I18N::number($years)); 9658add1155SRico Sonntag } 9668add1155SRico Sonntag 9678add1155SRico Sonntag /** 9688add1155SRico Sonntag * Find the oldest individuals. 9698add1155SRico Sonntag * 9708add1155SRico Sonntag * @param string $sex 9718add1155SRico Sonntag * @param int $total 9728add1155SRico Sonntag * 97391c84b80SGreg Roach * @return array<array<string,mixed>> 9748add1155SRico Sonntag */ 9758add1155SRico Sonntag private function topTenOldestQuery(string $sex, int $total): array 9768add1155SRico Sonntag { 97744cdc21eSGreg Roach $prefix = DB::connection()->getTablePrefix(); 9788add1155SRico Sonntag 97944cdc21eSGreg Roach $rows = $this->birthAndDeathQuery($sex) 98044cdc21eSGreg Roach ->groupBy(['i_id', 'i_file']) 98144cdc21eSGreg Roach ->orderBy('days', 'desc') 982a69f5655SGreg Roach ->select(['individuals.*', new Expression('MAX(' . $prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1) AS days')]) 98344cdc21eSGreg Roach ->take($total) 98444cdc21eSGreg Roach ->get(); 9858add1155SRico Sonntag 9868add1155SRico Sonntag $top10 = []; 9878add1155SRico Sonntag foreach ($rows as $row) { 98844cdc21eSGreg Roach /** @var Individual $individual */ 9896b9cb339SGreg Roach $individual = Registry::individualFactory()->mapper($this->tree)($row); 9908add1155SRico Sonntag 99144cdc21eSGreg Roach if ($individual->canShow()) { 9928add1155SRico Sonntag $top10[] = [ 99344cdc21eSGreg Roach 'person' => $individual, 99444cdc21eSGreg Roach 'age' => $this->calculateAge((int) $row->days), 9958add1155SRico Sonntag ]; 9968add1155SRico Sonntag } 9978add1155SRico Sonntag } 9988add1155SRico Sonntag 9998add1155SRico Sonntag return $top10; 10008add1155SRico Sonntag } 10018add1155SRico Sonntag 10028add1155SRico Sonntag /** 10038add1155SRico Sonntag * Find the oldest individuals. 10048add1155SRico Sonntag * 10058add1155SRico Sonntag * @param int $total 10068add1155SRico Sonntag * 10078add1155SRico Sonntag * @return string 10088add1155SRico Sonntag */ 10098add1155SRico Sonntag public function topTenOldest(int $total = 10): string 10108add1155SRico Sonntag { 10118add1155SRico Sonntag $records = $this->topTenOldestQuery('BOTH', $total); 10128add1155SRico Sonntag 1013c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 10148add1155SRico Sonntag 'records' => $records, 1015c0112ce8SGreg Roach ]); 10168add1155SRico Sonntag } 10178add1155SRico Sonntag 10188add1155SRico Sonntag /** 10198add1155SRico Sonntag * Find the oldest living individuals. 10208add1155SRico Sonntag * 10218add1155SRico Sonntag * @param int $total 10228add1155SRico Sonntag * 10238add1155SRico Sonntag * @return string 10248add1155SRico Sonntag */ 10258add1155SRico Sonntag public function topTenOldestList(int $total = 10): string 10268add1155SRico Sonntag { 10278add1155SRico Sonntag $records = $this->topTenOldestQuery('BOTH', $total); 10288add1155SRico Sonntag 1029c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 10308add1155SRico Sonntag 'records' => $records, 1031c0112ce8SGreg Roach ]); 10328add1155SRico Sonntag } 10338add1155SRico Sonntag 10348add1155SRico Sonntag /** 10358add1155SRico Sonntag * Find the oldest females. 10368add1155SRico Sonntag * 10378add1155SRico Sonntag * @param int $total 10388add1155SRico Sonntag * 10398add1155SRico Sonntag * @return string 10408add1155SRico Sonntag */ 10418add1155SRico Sonntag public function topTenOldestFemale(int $total = 10): string 10428add1155SRico Sonntag { 10438add1155SRico Sonntag $records = $this->topTenOldestQuery('F', $total); 10448add1155SRico Sonntag 1045c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 10468add1155SRico Sonntag 'records' => $records, 1047c0112ce8SGreg Roach ]); 10488add1155SRico Sonntag } 10498add1155SRico Sonntag 10508add1155SRico Sonntag /** 10518add1155SRico Sonntag * Find the oldest living females. 10528add1155SRico Sonntag * 10538add1155SRico Sonntag * @param int $total 10548add1155SRico Sonntag * 10558add1155SRico Sonntag * @return string 10568add1155SRico Sonntag */ 10578add1155SRico Sonntag public function topTenOldestFemaleList(int $total = 10): string 10588add1155SRico Sonntag { 10598add1155SRico Sonntag $records = $this->topTenOldestQuery('F', $total); 10608add1155SRico Sonntag 1061c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 10628add1155SRico Sonntag 'records' => $records, 1063c0112ce8SGreg Roach ]); 10648add1155SRico Sonntag } 10658add1155SRico Sonntag 10668add1155SRico Sonntag /** 10678add1155SRico Sonntag * Find the longest lived males. 10688add1155SRico Sonntag * 10698add1155SRico Sonntag * @param int $total 10708add1155SRico Sonntag * 10718add1155SRico Sonntag * @return string 10728add1155SRico Sonntag */ 10738add1155SRico Sonntag public function topTenOldestMale(int $total = 10): string 10748add1155SRico Sonntag { 10758add1155SRico Sonntag $records = $this->topTenOldestQuery('M', $total); 10768add1155SRico Sonntag 1077c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 10788add1155SRico Sonntag 'records' => $records, 1079c0112ce8SGreg Roach ]); 10808add1155SRico Sonntag } 10818add1155SRico Sonntag 10828add1155SRico Sonntag /** 10838add1155SRico Sonntag * Find the longest lived males. 10848add1155SRico Sonntag * 10858add1155SRico Sonntag * @param int $total 10868add1155SRico Sonntag * 10878add1155SRico Sonntag * @return string 10888add1155SRico Sonntag */ 10898add1155SRico Sonntag public function topTenOldestMaleList(int $total = 10): string 10908add1155SRico Sonntag { 10918add1155SRico Sonntag $records = $this->topTenOldestQuery('M', $total); 10928add1155SRico Sonntag 1093c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 10948add1155SRico Sonntag 'records' => $records, 1095c0112ce8SGreg Roach ]); 10968add1155SRico Sonntag } 10978add1155SRico Sonntag 10988add1155SRico Sonntag /** 10998add1155SRico Sonntag * Find the oldest living individuals. 11008add1155SRico Sonntag * 1101c0112ce8SGreg Roach * @param string $sex "M", "F" or "BOTH" 11028add1155SRico Sonntag * @param int $total 11038add1155SRico Sonntag * 110491c84b80SGreg Roach * @return array<array<string,mixed>> 11058add1155SRico Sonntag */ 1106c0112ce8SGreg Roach private function topTenOldestAliveQuery(string $sex, int $total): array 11078add1155SRico Sonntag { 1108d1a467e4SGreg Roach $query = DB::table('dates') 11090b5fd0a6SGreg Roach ->join('individuals', static function (JoinClause $join): void { 1110d1a467e4SGreg Roach $join 1111d1a467e4SGreg Roach ->on('i_id', '=', 'd_gid') 1112d1a467e4SGreg Roach ->on('i_file', '=', 'd_file'); 1113d1a467e4SGreg Roach }) 1114d1a467e4SGreg Roach ->where('d_file', '=', $this->tree->id()) 1115d1a467e4SGreg Roach ->where('d_julianday1', '<>', 0) 1116d1a467e4SGreg Roach ->where('d_fact', '=', 'BIRT') 1117d1a467e4SGreg Roach ->where('i_gedcom', 'NOT LIKE', "%\n1 DEAT%") 1118d1a467e4SGreg Roach ->where('i_gedcom', 'NOT LIKE', "%\n1 BURI%") 1119d1a467e4SGreg Roach ->where('i_gedcom', 'NOT LIKE', "%\n1 CREM%"); 1120d1a467e4SGreg Roach 1121d1a467e4SGreg Roach if ($sex === 'F' || $sex === 'M') { 1122d1a467e4SGreg Roach $query->where('i_sex', '=', $sex); 11238add1155SRico Sonntag } 11248add1155SRico Sonntag 1125c0112ce8SGreg Roach return $query 1126d1a467e4SGreg Roach ->groupBy(['i_id', 'i_file']) 1127a69f5655SGreg Roach ->orderBy(new Expression('MIN(d_julianday1)')) 112847256fc5SGreg Roach ->select(['individuals.*']) 1129d1a467e4SGreg Roach ->take($total) 1130d1a467e4SGreg Roach ->get() 11316b9cb339SGreg Roach ->map(Registry::individualFactory()->mapper($this->tree)) 1132c0112ce8SGreg Roach ->filter(GedcomRecord::accessFilter()) 1133c0112ce8SGreg Roach ->map(function (Individual $individual): array { 1134c0112ce8SGreg Roach return [ 1135d1a467e4SGreg Roach 'person' => $individual, 11364459dc9aSGreg Roach 'age' => $this->calculateAge(Carbon::now()->julianDay() - $individual->getBirthDate()->minimumJulianDay()), 11378add1155SRico Sonntag ]; 1138c0112ce8SGreg Roach }) 1139c0112ce8SGreg Roach ->all(); 11408add1155SRico Sonntag } 11418add1155SRico Sonntag 11428add1155SRico Sonntag /** 11438add1155SRico Sonntag * Find the oldest living individuals. 11448add1155SRico Sonntag * 11458add1155SRico Sonntag * @param int $total 11468add1155SRico Sonntag * 11478add1155SRico Sonntag * @return string 11488add1155SRico Sonntag */ 11498add1155SRico Sonntag public function topTenOldestAlive(int $total = 10): string 11508add1155SRico Sonntag { 11518add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 11528add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 11538add1155SRico Sonntag } 11548add1155SRico Sonntag 11558add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('BOTH', $total); 11568add1155SRico Sonntag 1157c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 11588add1155SRico Sonntag 'records' => $records, 1159c0112ce8SGreg Roach ]); 11608add1155SRico Sonntag } 11618add1155SRico Sonntag 11628add1155SRico Sonntag /** 11638add1155SRico Sonntag * Find the oldest living individuals. 11648add1155SRico Sonntag * 11658add1155SRico Sonntag * @param int $total 11668add1155SRico Sonntag * 11678add1155SRico Sonntag * @return string 11688add1155SRico Sonntag */ 11698add1155SRico Sonntag public function topTenOldestListAlive(int $total = 10): string 11708add1155SRico Sonntag { 11718add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 11728add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 11738add1155SRico Sonntag } 11748add1155SRico Sonntag 11758add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('BOTH', $total); 11768add1155SRico Sonntag 1177c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 11788add1155SRico Sonntag 'records' => $records, 1179c0112ce8SGreg Roach ]); 11808add1155SRico Sonntag } 11818add1155SRico Sonntag 11828add1155SRico Sonntag /** 11838add1155SRico Sonntag * Find the oldest living females. 11848add1155SRico Sonntag * 11858add1155SRico Sonntag * @param int $total 11868add1155SRico Sonntag * 11878add1155SRico Sonntag * @return string 11888add1155SRico Sonntag */ 11898add1155SRico Sonntag public function topTenOldestFemaleAlive(int $total = 10): string 11908add1155SRico Sonntag { 11918add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 11928add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 11938add1155SRico Sonntag } 11948add1155SRico Sonntag 11958add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('F', $total); 11968add1155SRico Sonntag 1197c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 11988add1155SRico Sonntag 'records' => $records, 1199c0112ce8SGreg Roach ]); 12008add1155SRico Sonntag } 12018add1155SRico Sonntag 12028add1155SRico Sonntag /** 12038add1155SRico Sonntag * Find the oldest living females. 12048add1155SRico Sonntag * 12058add1155SRico Sonntag * @param int $total 12068add1155SRico Sonntag * 12078add1155SRico Sonntag * @return string 12088add1155SRico Sonntag */ 12098add1155SRico Sonntag public function topTenOldestFemaleListAlive(int $total = 10): string 12108add1155SRico Sonntag { 12118add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 12128add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 12138add1155SRico Sonntag } 12148add1155SRico Sonntag 12158add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('F', $total); 12168add1155SRico Sonntag 1217c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 12188add1155SRico Sonntag 'records' => $records, 1219c0112ce8SGreg Roach ]); 12208add1155SRico Sonntag } 12218add1155SRico Sonntag 12228add1155SRico Sonntag /** 12238add1155SRico Sonntag * Find the longest lived living males. 12248add1155SRico Sonntag * 12258add1155SRico Sonntag * @param int $total 12268add1155SRico Sonntag * 12278add1155SRico Sonntag * @return string 12288add1155SRico Sonntag */ 12298add1155SRico Sonntag public function topTenOldestMaleAlive(int $total = 10): string 12308add1155SRico Sonntag { 12318add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 12328add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 12338add1155SRico Sonntag } 12348add1155SRico Sonntag 12358add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('M', $total); 12368add1155SRico Sonntag 1237c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 12388add1155SRico Sonntag 'records' => $records, 1239c0112ce8SGreg Roach ]); 12408add1155SRico Sonntag } 12418add1155SRico Sonntag 12428add1155SRico Sonntag /** 12438add1155SRico Sonntag * Find the longest lived living males. 12448add1155SRico Sonntag * 12458add1155SRico Sonntag * @param int $total 12468add1155SRico Sonntag * 12478add1155SRico Sonntag * @return string 12488add1155SRico Sonntag */ 12498add1155SRico Sonntag public function topTenOldestMaleListAlive(int $total = 10): string 12508add1155SRico Sonntag { 12518add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 12528add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 12538add1155SRico Sonntag } 12548add1155SRico Sonntag 12558add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('M', $total); 12568add1155SRico Sonntag 1257c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 12588add1155SRico Sonntag 'records' => $records, 1259c0112ce8SGreg Roach ]); 12608add1155SRico Sonntag } 12618add1155SRico Sonntag 12628add1155SRico Sonntag /** 12638add1155SRico Sonntag * Find the average lifespan. 12648add1155SRico Sonntag * 1265c0112ce8SGreg Roach * @param string $sex "M", "F" or "BOTH" 12668add1155SRico Sonntag * @param bool $show_years 12678add1155SRico Sonntag * 12688add1155SRico Sonntag * @return string 12698add1155SRico Sonntag */ 1270c0112ce8SGreg Roach private function averageLifespanQuery(string $sex, bool $show_years): string 12718add1155SRico Sonntag { 127244cdc21eSGreg Roach $prefix = DB::connection()->getTablePrefix(); 12738add1155SRico Sonntag 127444cdc21eSGreg Roach $days = (int) $this->birthAndDeathQuery($sex) 1275a69f5655SGreg Roach ->select(new Expression('AVG(' . $prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1) AS days')) 127644cdc21eSGreg Roach ->value('days'); 12778add1155SRico Sonntag 12788add1155SRico Sonntag if ($show_years) { 127944cdc21eSGreg Roach return $this->calculateAge($days); 12808add1155SRico Sonntag } 12818add1155SRico Sonntag 128244cdc21eSGreg Roach return I18N::number((int) ($days / 365.25)); 12838add1155SRico Sonntag } 12848add1155SRico Sonntag 12858add1155SRico Sonntag /** 12868add1155SRico Sonntag * Find the average lifespan. 12878add1155SRico Sonntag * 12888add1155SRico Sonntag * @param bool $show_years 12898add1155SRico Sonntag * 12908add1155SRico Sonntag * @return string 12918add1155SRico Sonntag */ 12928add1155SRico Sonntag public function averageLifespan($show_years = false): string 12938add1155SRico Sonntag { 12948add1155SRico Sonntag return $this->averageLifespanQuery('BOTH', $show_years); 12958add1155SRico Sonntag } 12968add1155SRico Sonntag 12978add1155SRico Sonntag /** 12988add1155SRico Sonntag * Find the average lifespan of females. 12998add1155SRico Sonntag * 13008add1155SRico Sonntag * @param bool $show_years 13018add1155SRico Sonntag * 13028add1155SRico Sonntag * @return string 13038add1155SRico Sonntag */ 13048add1155SRico Sonntag public function averageLifespanFemale($show_years = false): string 13058add1155SRico Sonntag { 13068add1155SRico Sonntag return $this->averageLifespanQuery('F', $show_years); 13078add1155SRico Sonntag } 13088add1155SRico Sonntag 13098add1155SRico Sonntag /** 13108add1155SRico Sonntag * Find the average male lifespan. 13118add1155SRico Sonntag * 13128add1155SRico Sonntag * @param bool $show_years 13138add1155SRico Sonntag * 13148add1155SRico Sonntag * @return string 13158add1155SRico Sonntag */ 13168add1155SRico Sonntag public function averageLifespanMale($show_years = false): string 13178add1155SRico Sonntag { 13188add1155SRico Sonntag return $this->averageLifespanQuery('M', $show_years); 13198add1155SRico Sonntag } 13208add1155SRico Sonntag 13218add1155SRico Sonntag /** 13228add1155SRico Sonntag * Convert totals into percentages. 13238add1155SRico Sonntag * 13248add1155SRico Sonntag * @param int $count 13258add1155SRico Sonntag * @param int $total 13268add1155SRico Sonntag * 13278add1155SRico Sonntag * @return string 13288add1155SRico Sonntag */ 13298add1155SRico Sonntag private function getPercentage(int $count, int $total): string 13308add1155SRico Sonntag { 1331f24db0ceSRico Sonntag return ($total !== 0) ? I18N::percentage($count / $total, 1) : ''; 13328add1155SRico Sonntag } 13338add1155SRico Sonntag 13348add1155SRico Sonntag /** 13358add1155SRico Sonntag * Returns how many individuals exist in the tree. 13368add1155SRico Sonntag * 13378add1155SRico Sonntag * @return int 13388add1155SRico Sonntag */ 13398add1155SRico Sonntag private function totalIndividualsQuery(): int 13408add1155SRico Sonntag { 13418add1155SRico Sonntag return DB::table('individuals') 13428add1155SRico Sonntag ->where('i_file', '=', $this->tree->id()) 13438add1155SRico Sonntag ->count(); 13448add1155SRico Sonntag } 13458add1155SRico Sonntag 13468add1155SRico Sonntag /** 13478add1155SRico Sonntag * Count the number of living individuals. 13488add1155SRico Sonntag * 13498add1155SRico Sonntag * The totalLiving/totalDeceased queries assume that every dead person will 13508add1155SRico Sonntag * have a DEAT record. It will not include individuals who were born more 13518add1155SRico Sonntag * than MAX_ALIVE_AGE years ago, and who have no DEAT record. 13528add1155SRico Sonntag * A good reason to run the “Add missing DEAT records” batch-update! 13538add1155SRico Sonntag * 13548add1155SRico Sonntag * @return int 13558add1155SRico Sonntag */ 13568add1155SRico Sonntag private function totalLivingQuery(): int 13578add1155SRico Sonntag { 13583dc8167dSGreg Roach $query = DB::table('individuals') 13593dc8167dSGreg Roach ->where('i_file', '=', $this->tree->id()); 13603dc8167dSGreg Roach 13613dc8167dSGreg Roach foreach (Gedcom::DEATH_EVENTS as $death_event) { 136268d9d7c9SRico Sonntag $query->where('i_gedcom', 'NOT LIKE', "%\n1 " . $death_event . '%'); 13633dc8167dSGreg Roach } 13643dc8167dSGreg Roach 13653dc8167dSGreg Roach return $query->count(); 13668add1155SRico Sonntag } 13678add1155SRico Sonntag 13688add1155SRico Sonntag /** 13698add1155SRico Sonntag * Count the number of dead individuals. 13708add1155SRico Sonntag * 13718add1155SRico Sonntag * @return int 13728add1155SRico Sonntag */ 13738add1155SRico Sonntag private function totalDeceasedQuery(): int 13748add1155SRico Sonntag { 13758add1155SRico Sonntag return DB::table('individuals') 13768add1155SRico Sonntag ->where('i_file', '=', $this->tree->id()) 13770b5fd0a6SGreg Roach ->where(static function (Builder $query): void { 13783dc8167dSGreg Roach foreach (Gedcom::DEATH_EVENTS as $death_event) { 137968d9d7c9SRico Sonntag $query->orWhere('i_gedcom', 'LIKE', "%\n1 " . $death_event . '%'); 13803dc8167dSGreg Roach } 13813dc8167dSGreg Roach }) 13828add1155SRico Sonntag ->count(); 13838add1155SRico Sonntag } 13848add1155SRico Sonntag 13858add1155SRico Sonntag /** 13868add1155SRico Sonntag * Returns the total count of a specific sex. 13878add1155SRico Sonntag * 13888add1155SRico Sonntag * @param string $sex The sex to query 13898add1155SRico Sonntag * 13908add1155SRico Sonntag * @return int 13918add1155SRico Sonntag */ 13928add1155SRico Sonntag private function getTotalSexQuery(string $sex): int 13938add1155SRico Sonntag { 13948add1155SRico Sonntag return DB::table('individuals') 13958add1155SRico Sonntag ->where('i_file', '=', $this->tree->id()) 13968add1155SRico Sonntag ->where('i_sex', '=', $sex) 13978add1155SRico Sonntag ->count(); 13988add1155SRico Sonntag } 13998add1155SRico Sonntag 14008add1155SRico Sonntag /** 14018add1155SRico Sonntag * Returns the total number of males. 14028add1155SRico Sonntag * 14038add1155SRico Sonntag * @return int 14048add1155SRico Sonntag */ 14058add1155SRico Sonntag private function totalSexMalesQuery(): int 14068add1155SRico Sonntag { 14078add1155SRico Sonntag return $this->getTotalSexQuery('M'); 14088add1155SRico Sonntag } 14098add1155SRico Sonntag 14108add1155SRico Sonntag /** 14118add1155SRico Sonntag * Returns the total number of females. 14128add1155SRico Sonntag * 14138add1155SRico Sonntag * @return int 14148add1155SRico Sonntag */ 14158add1155SRico Sonntag private function totalSexFemalesQuery(): int 14168add1155SRico Sonntag { 14178add1155SRico Sonntag return $this->getTotalSexQuery('F'); 14188add1155SRico Sonntag } 14198add1155SRico Sonntag 14208add1155SRico Sonntag /** 14218add1155SRico Sonntag * Returns the total number of individuals with unknown sex. 14228add1155SRico Sonntag * 14238add1155SRico Sonntag * @return int 14248add1155SRico Sonntag */ 14258add1155SRico Sonntag private function totalSexUnknownQuery(): int 14268add1155SRico Sonntag { 14278add1155SRico Sonntag return $this->getTotalSexQuery('U'); 14288add1155SRico Sonntag } 14298add1155SRico Sonntag 14308add1155SRico Sonntag /** 14318add1155SRico Sonntag * Count the total families. 14328add1155SRico Sonntag * 14338add1155SRico Sonntag * @return int 14348add1155SRico Sonntag */ 14358add1155SRico Sonntag private function totalFamiliesQuery(): int 14368add1155SRico Sonntag { 14378add1155SRico Sonntag return DB::table('families') 14388add1155SRico Sonntag ->where('f_file', '=', $this->tree->id()) 14398add1155SRico Sonntag ->count(); 14408add1155SRico Sonntag } 14418add1155SRico Sonntag 14428add1155SRico Sonntag /** 14438add1155SRico Sonntag * How many individuals have one or more sources. 14448add1155SRico Sonntag * 14458add1155SRico Sonntag * @return int 14468add1155SRico Sonntag */ 14478add1155SRico Sonntag private function totalIndisWithSourcesQuery(): int 14488add1155SRico Sonntag { 14498add1155SRico Sonntag return DB::table('individuals') 14508add1155SRico Sonntag ->select(['i_id']) 14518add1155SRico Sonntag ->distinct() 14520b5fd0a6SGreg Roach ->join('link', static function (JoinClause $join): void { 14538add1155SRico Sonntag $join->on('i_id', '=', 'l_from') 14548add1155SRico Sonntag ->on('i_file', '=', 'l_file'); 14558add1155SRico Sonntag }) 14568add1155SRico Sonntag ->where('l_file', '=', $this->tree->id()) 14578add1155SRico Sonntag ->where('l_type', '=', 'SOUR') 14588add1155SRico Sonntag ->count('i_id'); 14598add1155SRico Sonntag } 14608add1155SRico Sonntag 14618add1155SRico Sonntag /** 14628add1155SRico Sonntag * Count the families with source records. 14638add1155SRico Sonntag * 14648add1155SRico Sonntag * @return int 14658add1155SRico Sonntag */ 14668add1155SRico Sonntag private function totalFamsWithSourcesQuery(): int 14678add1155SRico Sonntag { 14688add1155SRico Sonntag return DB::table('families') 14698add1155SRico Sonntag ->select(['f_id']) 14708add1155SRico Sonntag ->distinct() 14710b5fd0a6SGreg Roach ->join('link', static function (JoinClause $join): void { 14728add1155SRico Sonntag $join->on('f_id', '=', 'l_from') 14738add1155SRico Sonntag ->on('f_file', '=', 'l_file'); 14748add1155SRico Sonntag }) 14758add1155SRico Sonntag ->where('l_file', '=', $this->tree->id()) 14768add1155SRico Sonntag ->where('l_type', '=', 'SOUR') 14778add1155SRico Sonntag ->count('f_id'); 14788add1155SRico Sonntag } 14798add1155SRico Sonntag 14808add1155SRico Sonntag /** 14818add1155SRico Sonntag * Count the number of repositories. 14828add1155SRico Sonntag * 14838add1155SRico Sonntag * @return int 14848add1155SRico Sonntag */ 14858add1155SRico Sonntag private function totalRepositoriesQuery(): int 14868add1155SRico Sonntag { 14878add1155SRico Sonntag return DB::table('other') 14888add1155SRico Sonntag ->where('o_file', '=', $this->tree->id()) 14898add1155SRico Sonntag ->where('o_type', '=', 'REPO') 14908add1155SRico Sonntag ->count(); 14918add1155SRico Sonntag } 14928add1155SRico Sonntag 14938add1155SRico Sonntag /** 14948add1155SRico Sonntag * Count the total number of sources. 14958add1155SRico Sonntag * 14968add1155SRico Sonntag * @return int 14978add1155SRico Sonntag */ 14988add1155SRico Sonntag private function totalSourcesQuery(): int 14998add1155SRico Sonntag { 15008add1155SRico Sonntag return DB::table('sources') 15018add1155SRico Sonntag ->where('s_file', '=', $this->tree->id()) 15028add1155SRico Sonntag ->count(); 15038add1155SRico Sonntag } 15048add1155SRico Sonntag 15058add1155SRico Sonntag /** 15068add1155SRico Sonntag * Count the number of notes. 15078add1155SRico Sonntag * 15088add1155SRico Sonntag * @return int 15098add1155SRico Sonntag */ 15108add1155SRico Sonntag private function totalNotesQuery(): int 15118add1155SRico Sonntag { 15128add1155SRico Sonntag return DB::table('other') 15138add1155SRico Sonntag ->where('o_file', '=', $this->tree->id()) 15148add1155SRico Sonntag ->where('o_type', '=', 'NOTE') 15158add1155SRico Sonntag ->count(); 15168add1155SRico Sonntag } 15178add1155SRico Sonntag 15188add1155SRico Sonntag /** 15198add1155SRico Sonntag * Returns the total number of records. 15208add1155SRico Sonntag * 15218add1155SRico Sonntag * @return int 15228add1155SRico Sonntag */ 15238add1155SRico Sonntag private function totalRecordsQuery(): int 15248add1155SRico Sonntag { 15258add1155SRico Sonntag return $this->totalIndividualsQuery() 15268add1155SRico Sonntag + $this->totalFamiliesQuery() 15278add1155SRico Sonntag + $this->totalNotesQuery() 15288add1155SRico Sonntag + $this->totalRepositoriesQuery() 15298add1155SRico Sonntag + $this->totalSourcesQuery(); 15308add1155SRico Sonntag } 15318add1155SRico Sonntag 15328add1155SRico Sonntag /** 15330dcd9387SGreg Roach * @return string 15348add1155SRico Sonntag */ 15358add1155SRico Sonntag public function totalRecords(): string 15368add1155SRico Sonntag { 15378add1155SRico Sonntag return I18N::number($this->totalRecordsQuery()); 15388add1155SRico Sonntag } 15398add1155SRico Sonntag 15408add1155SRico Sonntag /** 15410dcd9387SGreg Roach * @return string 15428add1155SRico Sonntag */ 15438add1155SRico Sonntag public function totalIndividuals(): string 15448add1155SRico Sonntag { 15458add1155SRico Sonntag return I18N::number($this->totalIndividualsQuery()); 15468add1155SRico Sonntag } 15478add1155SRico Sonntag 15488add1155SRico Sonntag /** 15498add1155SRico Sonntag * Count the number of living individuals. 15508add1155SRico Sonntag * 15518add1155SRico Sonntag * @return string 15528add1155SRico Sonntag */ 15538add1155SRico Sonntag public function totalLiving(): string 15548add1155SRico Sonntag { 15558add1155SRico Sonntag return I18N::number($this->totalLivingQuery()); 15568add1155SRico Sonntag } 15578add1155SRico Sonntag 15588add1155SRico Sonntag /** 15598add1155SRico Sonntag * Count the number of dead individuals. 15608add1155SRico Sonntag * 15618add1155SRico Sonntag * @return string 15628add1155SRico Sonntag */ 15638add1155SRico Sonntag public function totalDeceased(): string 15648add1155SRico Sonntag { 15658add1155SRico Sonntag return I18N::number($this->totalDeceasedQuery()); 15668add1155SRico Sonntag } 15678add1155SRico Sonntag 15688add1155SRico Sonntag /** 15690dcd9387SGreg Roach * @return string 15708add1155SRico Sonntag */ 15718add1155SRico Sonntag public function totalSexMales(): string 15728add1155SRico Sonntag { 15738add1155SRico Sonntag return I18N::number($this->totalSexMalesQuery()); 15748add1155SRico Sonntag } 15758add1155SRico Sonntag 15768add1155SRico Sonntag /** 15770dcd9387SGreg Roach * @return string 15788add1155SRico Sonntag */ 15798add1155SRico Sonntag public function totalSexFemales(): string 15808add1155SRico Sonntag { 15818add1155SRico Sonntag return I18N::number($this->totalSexFemalesQuery()); 15828add1155SRico Sonntag } 15838add1155SRico Sonntag 15848add1155SRico Sonntag /** 15850dcd9387SGreg Roach * @return string 15868add1155SRico Sonntag */ 15878add1155SRico Sonntag public function totalSexUnknown(): string 15888add1155SRico Sonntag { 15898add1155SRico Sonntag return I18N::number($this->totalSexUnknownQuery()); 15908add1155SRico Sonntag } 15918add1155SRico Sonntag 15928add1155SRico Sonntag /** 15930dcd9387SGreg Roach * @return string 15948add1155SRico Sonntag */ 15958add1155SRico Sonntag public function totalFamilies(): string 15968add1155SRico Sonntag { 15978add1155SRico Sonntag return I18N::number($this->totalFamiliesQuery()); 15988add1155SRico Sonntag } 15998add1155SRico Sonntag 16008add1155SRico Sonntag /** 16018add1155SRico Sonntag * How many individuals have one or more sources. 16028add1155SRico Sonntag * 16038add1155SRico Sonntag * @return string 16048add1155SRico Sonntag */ 16058add1155SRico Sonntag public function totalIndisWithSources(): string 16068add1155SRico Sonntag { 16078add1155SRico Sonntag return I18N::number($this->totalIndisWithSourcesQuery()); 16088add1155SRico Sonntag } 16098add1155SRico Sonntag 16108add1155SRico Sonntag /** 16118add1155SRico Sonntag * Count the families with with source records. 16128add1155SRico Sonntag * 16138add1155SRico Sonntag * @return string 16148add1155SRico Sonntag */ 16158add1155SRico Sonntag public function totalFamsWithSources(): string 16168add1155SRico Sonntag { 16178add1155SRico Sonntag return I18N::number($this->totalFamsWithSourcesQuery()); 16188add1155SRico Sonntag } 16198add1155SRico Sonntag 16208add1155SRico Sonntag /** 16210dcd9387SGreg Roach * @return string 16228add1155SRico Sonntag */ 16238add1155SRico Sonntag public function totalRepositories(): string 16248add1155SRico Sonntag { 16258add1155SRico Sonntag return I18N::number($this->totalRepositoriesQuery()); 16268add1155SRico Sonntag } 16278add1155SRico Sonntag 16288add1155SRico Sonntag /** 16290dcd9387SGreg Roach * @return string 16308add1155SRico Sonntag */ 16318add1155SRico Sonntag public function totalSources(): string 16328add1155SRico Sonntag { 16338add1155SRico Sonntag return I18N::number($this->totalSourcesQuery()); 16348add1155SRico Sonntag } 16358add1155SRico Sonntag 16368add1155SRico Sonntag /** 16370dcd9387SGreg Roach * @return string 16388add1155SRico Sonntag */ 16398add1155SRico Sonntag public function totalNotes(): string 16408add1155SRico Sonntag { 16418add1155SRico Sonntag return I18N::number($this->totalNotesQuery()); 16428add1155SRico Sonntag } 16438add1155SRico Sonntag 16448add1155SRico Sonntag /** 16450dcd9387SGreg Roach * @return string 16468add1155SRico Sonntag */ 16478add1155SRico Sonntag public function totalIndividualsPercentage(): string 16488add1155SRico Sonntag { 16498add1155SRico Sonntag return $this->getPercentage( 16508add1155SRico Sonntag $this->totalIndividualsQuery(), 16518add1155SRico Sonntag $this->totalRecordsQuery() 16528add1155SRico Sonntag ); 16538add1155SRico Sonntag } 16548add1155SRico Sonntag 16558add1155SRico Sonntag /** 16560dcd9387SGreg Roach * @return string 16578add1155SRico Sonntag */ 16588add1155SRico Sonntag public function totalFamiliesPercentage(): string 16598add1155SRico Sonntag { 16608add1155SRico Sonntag return $this->getPercentage( 16618add1155SRico Sonntag $this->totalFamiliesQuery(), 16628add1155SRico Sonntag $this->totalRecordsQuery() 16638add1155SRico Sonntag ); 16648add1155SRico Sonntag } 16658add1155SRico Sonntag 16668add1155SRico Sonntag /** 16670dcd9387SGreg Roach * @return string 16688add1155SRico Sonntag */ 16698add1155SRico Sonntag public function totalRepositoriesPercentage(): string 16708add1155SRico Sonntag { 16718add1155SRico Sonntag return $this->getPercentage( 16728add1155SRico Sonntag $this->totalRepositoriesQuery(), 16738add1155SRico Sonntag $this->totalRecordsQuery() 16748add1155SRico Sonntag ); 16758add1155SRico Sonntag } 16768add1155SRico Sonntag 16778add1155SRico Sonntag /** 16780dcd9387SGreg Roach * @return string 16798add1155SRico Sonntag */ 16808add1155SRico Sonntag public function totalSourcesPercentage(): string 16818add1155SRico Sonntag { 16828add1155SRico Sonntag return $this->getPercentage( 16838add1155SRico Sonntag $this->totalSourcesQuery(), 16848add1155SRico Sonntag $this->totalRecordsQuery() 16858add1155SRico Sonntag ); 16868add1155SRico Sonntag } 16878add1155SRico Sonntag 16888add1155SRico Sonntag /** 16890dcd9387SGreg Roach * @return string 16908add1155SRico Sonntag */ 16918add1155SRico Sonntag public function totalNotesPercentage(): string 16928add1155SRico Sonntag { 16938add1155SRico Sonntag return $this->getPercentage( 16948add1155SRico Sonntag $this->totalNotesQuery(), 16958add1155SRico Sonntag $this->totalRecordsQuery() 16968add1155SRico Sonntag ); 16978add1155SRico Sonntag } 16988add1155SRico Sonntag 16998add1155SRico Sonntag /** 17000dcd9387SGreg Roach * @return string 17018add1155SRico Sonntag */ 17028add1155SRico Sonntag public function totalLivingPercentage(): string 17038add1155SRico Sonntag { 17048add1155SRico Sonntag return $this->getPercentage( 17058add1155SRico Sonntag $this->totalLivingQuery(), 17068add1155SRico Sonntag $this->totalIndividualsQuery() 17078add1155SRico Sonntag ); 17088add1155SRico Sonntag } 17098add1155SRico Sonntag 17108add1155SRico Sonntag /** 17110dcd9387SGreg Roach * @return string 17128add1155SRico Sonntag */ 17138add1155SRico Sonntag public function totalDeceasedPercentage(): string 17148add1155SRico Sonntag { 17158add1155SRico Sonntag return $this->getPercentage( 17168add1155SRico Sonntag $this->totalDeceasedQuery(), 17178add1155SRico Sonntag $this->totalIndividualsQuery() 17188add1155SRico Sonntag ); 17198add1155SRico Sonntag } 17208add1155SRico Sonntag 17218add1155SRico Sonntag /** 17220dcd9387SGreg Roach * @return string 17238add1155SRico Sonntag */ 17248add1155SRico Sonntag public function totalSexMalesPercentage(): string 17258add1155SRico Sonntag { 17268add1155SRico Sonntag return $this->getPercentage( 17278add1155SRico Sonntag $this->totalSexMalesQuery(), 17288add1155SRico Sonntag $this->totalIndividualsQuery() 17298add1155SRico Sonntag ); 17308add1155SRico Sonntag } 17318add1155SRico Sonntag 17328add1155SRico Sonntag /** 17330dcd9387SGreg Roach * @return string 17348add1155SRico Sonntag */ 17358add1155SRico Sonntag public function totalSexFemalesPercentage(): string 17368add1155SRico Sonntag { 17378add1155SRico Sonntag return $this->getPercentage( 17388add1155SRico Sonntag $this->totalSexFemalesQuery(), 17398add1155SRico Sonntag $this->totalIndividualsQuery() 17408add1155SRico Sonntag ); 17418add1155SRico Sonntag } 17428add1155SRico Sonntag 17438add1155SRico Sonntag /** 17440dcd9387SGreg Roach * @return string 17458add1155SRico Sonntag */ 17468add1155SRico Sonntag public function totalSexUnknownPercentage(): string 17478add1155SRico Sonntag { 17488add1155SRico Sonntag return $this->getPercentage( 17498add1155SRico Sonntag $this->totalSexUnknownQuery(), 17508add1155SRico Sonntag $this->totalIndividualsQuery() 17518add1155SRico Sonntag ); 17528add1155SRico Sonntag } 17538add1155SRico Sonntag 17548add1155SRico Sonntag /** 17558add1155SRico Sonntag * Create a chart of common given names. 17568add1155SRico Sonntag * 17578add1155SRico Sonntag * @param string|null $color_from 17588add1155SRico Sonntag * @param string|null $color_to 17598add1155SRico Sonntag * @param int $maxtoshow 17608add1155SRico Sonntag * 17618add1155SRico Sonntag * @return string 17628add1155SRico Sonntag */ 17638add1155SRico Sonntag public function chartCommonGiven( 17648add1155SRico Sonntag string $color_from = null, 17658add1155SRico Sonntag string $color_to = null, 17668add1155SRico Sonntag int $maxtoshow = 7 17678add1155SRico Sonntag ): string { 17688add1155SRico Sonntag $tot_indi = $this->totalIndividualsQuery(); 17698add1155SRico Sonntag $given = $this->commonGivenQuery('B', 'chart', false, 1, $maxtoshow); 17708add1155SRico Sonntag 1771320f6a24SGreg Roach if ($given === []) { 1772dd7dd2a1SRico Sonntag return I18N::translate('This information is not available.'); 177388de55fdSRico Sonntag } 177488de55fdSRico Sonntag 177593ccd686SRico Sonntag return (new ChartCommonGiven()) 177688de55fdSRico Sonntag ->chartCommonGiven($tot_indi, $given, $color_from, $color_to); 17778add1155SRico Sonntag } 17788add1155SRico Sonntag 17798add1155SRico Sonntag /** 17808add1155SRico Sonntag * Create a chart of common surnames. 17818add1155SRico Sonntag * 17828add1155SRico Sonntag * @param string|null $color_from 17838add1155SRico Sonntag * @param string|null $color_to 17848add1155SRico Sonntag * @param int $number_of_surnames 17858add1155SRico Sonntag * 17868add1155SRico Sonntag * @return string 17878add1155SRico Sonntag */ 17888add1155SRico Sonntag public function chartCommonSurnames( 17898add1155SRico Sonntag string $color_from = null, 17908add1155SRico Sonntag string $color_to = null, 17918add1155SRico Sonntag int $number_of_surnames = 10 17928add1155SRico Sonntag ): string { 17938add1155SRico Sonntag $tot_indi = $this->totalIndividualsQuery(); 17948add1155SRico Sonntag $all_surnames = $this->topSurnames($number_of_surnames, 0); 17958add1155SRico Sonntag 1796320f6a24SGreg Roach if ($all_surnames === []) { 1797dd7dd2a1SRico Sonntag return I18N::translate('This information is not available.'); 179888de55fdSRico Sonntag } 179988de55fdSRico Sonntag 18008add1155SRico Sonntag return (new ChartCommonSurname($this->tree)) 180188de55fdSRico Sonntag ->chartCommonSurnames($tot_indi, $all_surnames, $color_from, $color_to); 18028add1155SRico Sonntag } 18038add1155SRico Sonntag 18048add1155SRico Sonntag /** 18058add1155SRico Sonntag * Create a chart showing mortality. 18068add1155SRico Sonntag * 18078add1155SRico Sonntag * @param string|null $color_living 18088add1155SRico Sonntag * @param string|null $color_dead 18098add1155SRico Sonntag * 18108add1155SRico Sonntag * @return string 18118add1155SRico Sonntag */ 181288de55fdSRico Sonntag public function chartMortality(string $color_living = null, string $color_dead = null): string 18138add1155SRico Sonntag { 18148add1155SRico Sonntag $tot_l = $this->totalLivingQuery(); 18158add1155SRico Sonntag $tot_d = $this->totalDeceasedQuery(); 18168add1155SRico Sonntag 181793ccd686SRico Sonntag return (new ChartMortality()) 181888de55fdSRico Sonntag ->chartMortality($tot_l, $tot_d, $color_living, $color_dead); 18198add1155SRico Sonntag } 18208add1155SRico Sonntag 18218add1155SRico Sonntag /** 18228add1155SRico Sonntag * Create a chart showing individuals with/without sources. 18238add1155SRico Sonntag * 18248add1155SRico Sonntag * @param string|null $color_from 18258add1155SRico Sonntag * @param string|null $color_to 18268add1155SRico Sonntag * 18278add1155SRico Sonntag * @return string 18288add1155SRico Sonntag */ 18298add1155SRico Sonntag public function chartIndisWithSources( 18308add1155SRico Sonntag string $color_from = null, 18318add1155SRico Sonntag string $color_to = null 18328add1155SRico Sonntag ): string { 18338add1155SRico Sonntag $tot_indi = $this->totalIndividualsQuery(); 18348add1155SRico Sonntag $tot_indi_source = $this->totalIndisWithSourcesQuery(); 18358add1155SRico Sonntag 183693ccd686SRico Sonntag return (new ChartIndividualWithSources()) 183788de55fdSRico Sonntag ->chartIndisWithSources($tot_indi, $tot_indi_source, $color_from, $color_to); 18388add1155SRico Sonntag } 18398add1155SRico Sonntag 18408add1155SRico Sonntag /** 18418add1155SRico Sonntag * Create a chart of individuals with/without sources. 18428add1155SRico Sonntag * 18438add1155SRico Sonntag * @param string|null $color_from 18448add1155SRico Sonntag * @param string|null $color_to 18458add1155SRico Sonntag * 18468add1155SRico Sonntag * @return string 18478add1155SRico Sonntag */ 18488add1155SRico Sonntag public function chartFamsWithSources( 18498add1155SRico Sonntag string $color_from = null, 18508add1155SRico Sonntag string $color_to = null 18518add1155SRico Sonntag ): string { 18528add1155SRico Sonntag $tot_fam = $this->totalFamiliesQuery(); 18538add1155SRico Sonntag $tot_fam_source = $this->totalFamsWithSourcesQuery(); 18548add1155SRico Sonntag 185593ccd686SRico Sonntag return (new ChartFamilyWithSources()) 185688de55fdSRico Sonntag ->chartFamsWithSources($tot_fam, $tot_fam_source, $color_from, $color_to); 18578add1155SRico Sonntag } 18588add1155SRico Sonntag 18598add1155SRico Sonntag /** 18600dcd9387SGreg Roach * @param string|null $color_female 18610dcd9387SGreg Roach * @param string|null $color_male 18620dcd9387SGreg Roach * @param string|null $color_unknown 18630dcd9387SGreg Roach * 18640dcd9387SGreg Roach * @return string 18658add1155SRico Sonntag */ 18668add1155SRico Sonntag public function chartSex( 18678add1155SRico Sonntag string $color_female = null, 18688add1155SRico Sonntag string $color_male = null, 18698add1155SRico Sonntag string $color_unknown = null 18708add1155SRico Sonntag ): string { 18718add1155SRico Sonntag $tot_m = $this->totalSexMalesQuery(); 18728add1155SRico Sonntag $tot_f = $this->totalSexFemalesQuery(); 18738add1155SRico Sonntag $tot_u = $this->totalSexUnknownQuery(); 18748add1155SRico Sonntag 187593ccd686SRico Sonntag return (new ChartSex()) 187688de55fdSRico Sonntag ->chartSex($tot_m, $tot_f, $tot_u, $color_female, $color_male, $color_unknown); 18778add1155SRico Sonntag } 187844cdc21eSGreg Roach 187944cdc21eSGreg Roach /** 188044cdc21eSGreg Roach * Query individuals, with their births and deaths. 188144cdc21eSGreg Roach * 188244cdc21eSGreg Roach * @param string $sex 188344cdc21eSGreg Roach * 188444cdc21eSGreg Roach * @return Builder 188544cdc21eSGreg Roach */ 1886e2cbf57aSGreg Roach private function birthAndDeathQuery(string $sex): Builder 1887e2cbf57aSGreg Roach { 188844cdc21eSGreg Roach $query = DB::table('individuals') 188944cdc21eSGreg Roach ->where('i_file', '=', $this->tree->id()) 18900b5fd0a6SGreg Roach ->join('dates AS birth', static function (JoinClause $join): void { 189144cdc21eSGreg Roach $join 189244cdc21eSGreg Roach ->on('birth.d_file', '=', 'i_file') 189344cdc21eSGreg Roach ->on('birth.d_gid', '=', 'i_id'); 189444cdc21eSGreg Roach }) 18950b5fd0a6SGreg Roach ->join('dates AS death', static function (JoinClause $join): void { 189644cdc21eSGreg Roach $join 189744cdc21eSGreg Roach ->on('death.d_file', '=', 'i_file') 189844cdc21eSGreg Roach ->on('death.d_gid', '=', 'i_id'); 189944cdc21eSGreg Roach }) 190044cdc21eSGreg Roach ->where('birth.d_fact', '=', 'BIRT') 190144cdc21eSGreg Roach ->where('death.d_fact', '=', 'DEAT') 190244cdc21eSGreg Roach ->whereColumn('death.d_julianday1', '>=', 'birth.d_julianday2') 190344cdc21eSGreg Roach ->where('birth.d_julianday2', '<>', 0); 190444cdc21eSGreg Roach 190544cdc21eSGreg Roach if ($sex === 'M' || $sex === 'F') { 190644cdc21eSGreg Roach $query->where('i_sex', '=', $sex); 190744cdc21eSGreg Roach } 190844cdc21eSGreg Roach 190944cdc21eSGreg Roach return $query; 191044cdc21eSGreg Roach } 19118add1155SRico Sonntag} 1912