xref: /webtrees/app/Statistics/Repository/IndividualRepository.php (revision 269fd10d8b81cb5e33a7a34320b7f88f5aec76eb)
18add1155SRico Sonntag<?php
28add1155SRico Sonntag/**
38add1155SRico Sonntag * webtrees: online genealogy
4242a7862SGreg Roach * Copyright (C) 2019 webtrees development team
58add1155SRico Sonntag * This program is free software: you can redistribute it and/or modify
68add1155SRico Sonntag * it under the terms of the GNU General Public License as published by
78add1155SRico Sonntag * the Free Software Foundation, either version 3 of the License, or
88add1155SRico Sonntag * (at your option) any later version.
98add1155SRico Sonntag * This program is distributed in the hope that it will be useful,
108add1155SRico Sonntag * but WITHOUT ANY WARRANTY; without even the implied warranty of
118add1155SRico Sonntag * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
128add1155SRico Sonntag * GNU General Public License for more details.
138add1155SRico Sonntag * You should have received a copy of the GNU General Public License
148add1155SRico Sonntag * along with this program. If not, see <http://www.gnu.org/licenses/>.
158add1155SRico Sonntag */
168add1155SRico Sonntagdeclare(strict_types=1);
178add1155SRico Sonntag
188add1155SRico Sonntagnamespace Fisharebest\Webtrees\Statistics\Repository;
198add1155SRico Sonntag
20*269fd10dSGreg Roachuse Carbon\Carbon;
218add1155SRico Sonntaguse Fisharebest\Webtrees\Auth;
228add1155SRico Sonntaguse Fisharebest\Webtrees\Functions\FunctionsDate;
238add1155SRico Sonntaguse Fisharebest\Webtrees\Functions\FunctionsPrintLists;
248add1155SRico Sonntaguse Fisharebest\Webtrees\Gedcom;
25d1a467e4SGreg Roachuse Fisharebest\Webtrees\GedcomRecord;
268add1155SRico Sonntaguse Fisharebest\Webtrees\I18N;
278add1155SRico Sonntaguse Fisharebest\Webtrees\Individual;
2867992b6aSRichard Cisseeuse Fisharebest\Webtrees\Module\IndividualListModule;
2967992b6aSRichard Cisseeuse Fisharebest\Webtrees\Module\ModuleInterface;
3087cca37cSGreg Roachuse Fisharebest\Webtrees\Module\ModuleListInterface;
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;
428add1155SRico Sonntaguse Fisharebest\Webtrees\Tree;
438add1155SRico Sonntaguse Illuminate\Database\Capsule\Manager as DB;
443dc8167dSGreg Roachuse Illuminate\Database\Query\Builder;
458add1155SRico Sonntaguse Illuminate\Database\Query\JoinClause;
468add1155SRico Sonntag
478add1155SRico Sonntag/**
488add1155SRico Sonntag *
498add1155SRico Sonntag */
508add1155SRico Sonntagclass IndividualRepository implements IndividualRepositoryInterface
518add1155SRico Sonntag{
528add1155SRico Sonntag    /**
538add1155SRico Sonntag     * @var Tree
548add1155SRico Sonntag     */
558add1155SRico Sonntag    private $tree;
568add1155SRico Sonntag
578add1155SRico Sonntag    /**
588add1155SRico Sonntag     * Constructor.
598add1155SRico Sonntag     *
608add1155SRico Sonntag     * @param Tree $tree
618add1155SRico Sonntag     */
628add1155SRico Sonntag    public function __construct(Tree $tree)
638add1155SRico Sonntag    {
648add1155SRico Sonntag        $this->tree = $tree;
658add1155SRico Sonntag    }
668add1155SRico Sonntag
678add1155SRico Sonntag    /**
688add1155SRico Sonntag     * Find common given names.
698add1155SRico Sonntag     *
708add1155SRico Sonntag     * @param string $sex
718add1155SRico Sonntag     * @param string $type
728add1155SRico Sonntag     * @param bool   $show_tot
738add1155SRico Sonntag     * @param int    $threshold
748add1155SRico Sonntag     * @param int    $maxtoshow
758add1155SRico Sonntag     *
768add1155SRico Sonntag     * @return string|int[]
778add1155SRico Sonntag     */
788add1155SRico Sonntag    private function commonGivenQuery(string $sex, string $type, bool $show_tot, int $threshold, int $maxtoshow)
798add1155SRico Sonntag    {
80d1a467e4SGreg Roach        $query = DB::table('name')
81d1a467e4SGreg Roach            ->join('individuals', function (JoinClause $join): void {
82d1a467e4SGreg Roach                $join
83d1a467e4SGreg Roach                    ->on('i_file', '=', 'n_file')
84d1a467e4SGreg Roach                    ->on('i_id', '=', 'n_id');
85d1a467e4SGreg Roach            })
86d1a467e4SGreg Roach            ->where('n_file', '=', $this->tree->id())
87d1a467e4SGreg Roach            ->where('n_type', '<>', '_MARNM')
88d1a467e4SGreg Roach            ->where('n_givn', '<>', '@P.N.')
89d1a467e4SGreg Roach            ->where(DB::raw('LENGTH(n_givn)'), '>', 1);
90d1a467e4SGreg Roach
918add1155SRico Sonntag        switch ($sex) {
928add1155SRico Sonntag            case 'M':
938add1155SRico Sonntag            case 'F':
948add1155SRico Sonntag            case 'U':
95d1a467e4SGreg Roach                $query->where('i_sex', '=', $sex);
968add1155SRico Sonntag                break;
97d1a467e4SGreg Roach
988add1155SRico Sonntag            case 'B':
998add1155SRico Sonntag            default:
100d1a467e4SGreg Roach                $query->where('i_sex', '<>', 'U');
1018add1155SRico Sonntag                break;
1028add1155SRico Sonntag        }
1038add1155SRico Sonntag
104d1a467e4SGreg Roach        $rows = $query
1053413ec75SRico Sonntag            ->groupBy(['n_givn'])
1063413ec75SRico Sonntag            ->select(['n_givn', DB::raw('COUNT(distinct n_id) AS count')])
107d1a467e4SGreg Roach            ->pluck('count', 'n_givn');
1088add1155SRico Sonntag
1098add1155SRico Sonntag        $nameList = [];
1108add1155SRico Sonntag
111d1a467e4SGreg Roach        foreach ($rows as $n_givn => $count) {
1128add1155SRico Sonntag            // Split “John Thomas” into “John” and “Thomas” and count against both totals
113d1a467e4SGreg Roach            foreach (explode(' ', $n_givn) as $given) {
1148add1155SRico Sonntag                // Exclude initials and particles.
1158add1155SRico Sonntag                if (!preg_match('/^([A-Z]|[a-z]{1,3})$/', $given)) {
1168add1155SRico Sonntag                    if (\array_key_exists($given, $nameList)) {
117d1a467e4SGreg Roach                        $nameList[$given] += (int) $count;
1188add1155SRico Sonntag                    } else {
119d1a467e4SGreg Roach                        $nameList[$given] = (int) $count;
1208add1155SRico Sonntag                    }
1218add1155SRico Sonntag                }
1228add1155SRico Sonntag            }
1238add1155SRico Sonntag        }
1248add1155SRico Sonntag        arsort($nameList);
1258add1155SRico Sonntag        $nameList = \array_slice($nameList, 0, $maxtoshow);
1268add1155SRico Sonntag
1278add1155SRico Sonntag        foreach ($nameList as $given => $total) {
1288add1155SRico Sonntag            if ($total < $threshold) {
1298add1155SRico Sonntag                unset($nameList[$given]);
1308add1155SRico Sonntag            }
1318add1155SRico Sonntag        }
1328add1155SRico Sonntag
1338add1155SRico Sonntag        switch ($type) {
1348add1155SRico Sonntag            case 'chart':
1358add1155SRico Sonntag                return $nameList;
1368add1155SRico Sonntag
1378add1155SRico Sonntag            case 'table':
1388add1155SRico Sonntag                return view('lists/given-names-table', [
1398add1155SRico Sonntag                    'given_names' => $nameList,
1408add1155SRico Sonntag                ]);
1418add1155SRico Sonntag
1428add1155SRico Sonntag            case 'list':
1438add1155SRico Sonntag                return view('lists/given-names-list', [
1448add1155SRico Sonntag                    'given_names' => $nameList,
1458add1155SRico Sonntag                    'show_totals' => $show_tot,
1468add1155SRico Sonntag                ]);
1478add1155SRico Sonntag
1488add1155SRico Sonntag            case 'nolist':
1498add1155SRico Sonntag            default:
1508add1155SRico Sonntag                array_walk($nameList, function (int &$value, string $key) use ($show_tot): void {
1518add1155SRico Sonntag                    if ($show_tot) {
1528add1155SRico Sonntag                        $value = '<span dir="auto">' . e($key);
1538add1155SRico Sonntag                    } else {
1548add1155SRico Sonntag                        $value = '<span dir="auto">' . e($key) . ' (' . I18N::number($value) . ')';
1558add1155SRico Sonntag                    }
1568add1155SRico Sonntag                });
1578add1155SRico Sonntag
1588add1155SRico Sonntag                return implode(I18N::$list_separator, $nameList);
1598add1155SRico Sonntag        }
1608add1155SRico Sonntag    }
1618add1155SRico Sonntag
1628add1155SRico Sonntag    /**
1638add1155SRico Sonntag     * Find common give names.
1648add1155SRico Sonntag     *
1658add1155SRico Sonntag     * @param int $threshold
1668add1155SRico Sonntag     * @param int $maxtoshow
1678add1155SRico Sonntag     *
1688add1155SRico Sonntag     * @return string
1698add1155SRico Sonntag     */
1708add1155SRico Sonntag    public function commonGiven(int $threshold = 1, int $maxtoshow = 10): string
1718add1155SRico Sonntag    {
1728add1155SRico Sonntag        return $this->commonGivenQuery('B', 'nolist', false, $threshold, $maxtoshow);
1738add1155SRico Sonntag    }
1748add1155SRico Sonntag
1758add1155SRico Sonntag    /**
1768add1155SRico Sonntag     * Find common give names.
1778add1155SRico Sonntag     *
1788add1155SRico Sonntag     * @param int $threshold
1798add1155SRico Sonntag     * @param int $maxtoshow
1808add1155SRico Sonntag     *
1818add1155SRico Sonntag     * @return string
1828add1155SRico Sonntag     */
1838add1155SRico Sonntag    public function commonGivenTotals(int $threshold = 1, int $maxtoshow = 10): string
1848add1155SRico Sonntag    {
1858add1155SRico Sonntag        return $this->commonGivenQuery('B', 'nolist', true, $threshold, $maxtoshow);
1868add1155SRico Sonntag    }
1878add1155SRico Sonntag
1888add1155SRico Sonntag    /**
1898add1155SRico Sonntag     * Find common give names.
1908add1155SRico Sonntag     *
1918add1155SRico Sonntag     * @param int $threshold
1928add1155SRico Sonntag     * @param int $maxtoshow
1938add1155SRico Sonntag     *
1948add1155SRico Sonntag     * @return string
1958add1155SRico Sonntag     */
1968add1155SRico Sonntag    public function commonGivenList(int $threshold = 1, int $maxtoshow = 10): string
1978add1155SRico Sonntag    {
1988add1155SRico Sonntag        return $this->commonGivenQuery('B', 'list', false, $threshold, $maxtoshow);
1998add1155SRico Sonntag    }
2008add1155SRico Sonntag
2018add1155SRico Sonntag    /**
2028add1155SRico Sonntag     * Find common give names.
2038add1155SRico Sonntag     *
2048add1155SRico Sonntag     * @param int $threshold
2058add1155SRico Sonntag     * @param int $maxtoshow
2068add1155SRico Sonntag     *
2078add1155SRico Sonntag     * @return string
2088add1155SRico Sonntag     */
2098add1155SRico Sonntag    public function commonGivenListTotals(int $threshold = 1, int $maxtoshow = 10): string
2108add1155SRico Sonntag    {
2118add1155SRico Sonntag        return $this->commonGivenQuery('B', 'list', true, $threshold, $maxtoshow);
2128add1155SRico Sonntag    }
2138add1155SRico Sonntag
2148add1155SRico Sonntag    /**
2158add1155SRico Sonntag     * Find common give names.
2168add1155SRico Sonntag     *
2178add1155SRico Sonntag     * @param int $threshold
2188add1155SRico Sonntag     * @param int $maxtoshow
2198add1155SRico Sonntag     *
2208add1155SRico Sonntag     * @return string
2218add1155SRico Sonntag     */
2228add1155SRico Sonntag    public function commonGivenTable(int $threshold = 1, int $maxtoshow = 10): string
2238add1155SRico Sonntag    {
2248add1155SRico Sonntag        return $this->commonGivenQuery('B', 'table', false, $threshold, $maxtoshow);
2258add1155SRico Sonntag    }
2268add1155SRico Sonntag
2278add1155SRico Sonntag    /**
2288add1155SRico Sonntag     * Find common give names of females.
2298add1155SRico Sonntag     *
2308add1155SRico Sonntag     * @param int $threshold
2318add1155SRico Sonntag     * @param int $maxtoshow
2328add1155SRico Sonntag     *
2338add1155SRico Sonntag     * @return string
2348add1155SRico Sonntag     */
2358add1155SRico Sonntag    public function commonGivenFemale(int $threshold = 1, int $maxtoshow = 10): string
2368add1155SRico Sonntag    {
2378add1155SRico Sonntag        return $this->commonGivenQuery('F', 'nolist', false, $threshold, $maxtoshow);
2388add1155SRico Sonntag    }
2398add1155SRico Sonntag
2408add1155SRico Sonntag    /**
2418add1155SRico Sonntag     * Find common give names of females.
2428add1155SRico Sonntag     *
2438add1155SRico Sonntag     * @param int $threshold
2448add1155SRico Sonntag     * @param int $maxtoshow
2458add1155SRico Sonntag     *
2468add1155SRico Sonntag     * @return string
2478add1155SRico Sonntag     */
2488add1155SRico Sonntag    public function commonGivenFemaleTotals(int $threshold = 1, int $maxtoshow = 10): string
2498add1155SRico Sonntag    {
2508add1155SRico Sonntag        return $this->commonGivenQuery('F', 'nolist', true, $threshold, $maxtoshow);
2518add1155SRico Sonntag    }
2528add1155SRico Sonntag
2538add1155SRico Sonntag    /**
2548add1155SRico Sonntag     * Find common give names of females.
2558add1155SRico Sonntag     *
2568add1155SRico Sonntag     * @param int $threshold
2578add1155SRico Sonntag     * @param int $maxtoshow
2588add1155SRico Sonntag     *
2598add1155SRico Sonntag     * @return string
2608add1155SRico Sonntag     */
2618add1155SRico Sonntag    public function commonGivenFemaleList(int $threshold = 1, int $maxtoshow = 10): string
2628add1155SRico Sonntag    {
2638add1155SRico Sonntag        return $this->commonGivenQuery('F', 'list', false, $threshold, $maxtoshow);
2648add1155SRico Sonntag    }
2658add1155SRico Sonntag
2668add1155SRico Sonntag    /**
2678add1155SRico Sonntag     * Find common give names of females.
2688add1155SRico Sonntag     *
2698add1155SRico Sonntag     * @param int $threshold
2708add1155SRico Sonntag     * @param int $maxtoshow
2718add1155SRico Sonntag     *
2728add1155SRico Sonntag     * @return string
2738add1155SRico Sonntag     */
2748add1155SRico Sonntag    public function commonGivenFemaleListTotals(int $threshold = 1, int $maxtoshow = 10): string
2758add1155SRico Sonntag    {
2768add1155SRico Sonntag        return $this->commonGivenQuery('F', 'list', true, $threshold, $maxtoshow);
2778add1155SRico Sonntag    }
2788add1155SRico Sonntag
2798add1155SRico Sonntag    /**
2808add1155SRico Sonntag     * Find common give names of females.
2818add1155SRico Sonntag     *
2828add1155SRico Sonntag     * @param int $threshold
2838add1155SRico Sonntag     * @param int $maxtoshow
2848add1155SRico Sonntag     *
2858add1155SRico Sonntag     * @return string
2868add1155SRico Sonntag     */
2878add1155SRico Sonntag    public function commonGivenFemaleTable(int $threshold = 1, int $maxtoshow = 10): string
2888add1155SRico Sonntag    {
2898add1155SRico Sonntag        return $this->commonGivenQuery('F', 'table', false, $threshold, $maxtoshow);
2908add1155SRico Sonntag    }
2918add1155SRico Sonntag
2928add1155SRico Sonntag    /**
2938add1155SRico Sonntag     * Find common give names of males.
2948add1155SRico Sonntag     *
2958add1155SRico Sonntag     * @param int $threshold
2968add1155SRico Sonntag     * @param int $maxtoshow
2978add1155SRico Sonntag     *
2988add1155SRico Sonntag     * @return string
2998add1155SRico Sonntag     */
3008add1155SRico Sonntag    public function commonGivenMale(int $threshold = 1, int $maxtoshow = 10): string
3018add1155SRico Sonntag    {
3028add1155SRico Sonntag        return $this->commonGivenQuery('M', 'nolist', false, $threshold, $maxtoshow);
3038add1155SRico Sonntag    }
3048add1155SRico Sonntag
3058add1155SRico Sonntag    /**
3068add1155SRico Sonntag     * Find common give names of males.
3078add1155SRico Sonntag     *
3088add1155SRico Sonntag     * @param int $threshold
3098add1155SRico Sonntag     * @param int $maxtoshow
3108add1155SRico Sonntag     *
3118add1155SRico Sonntag     * @return string
3128add1155SRico Sonntag     */
3138add1155SRico Sonntag    public function commonGivenMaleTotals(int $threshold = 1, int $maxtoshow = 10): string
3148add1155SRico Sonntag    {
3158add1155SRico Sonntag        return $this->commonGivenQuery('M', 'nolist', true, $threshold, $maxtoshow);
3168add1155SRico Sonntag    }
3178add1155SRico Sonntag
3188add1155SRico Sonntag    /**
3198add1155SRico Sonntag     * Find common give names of males.
3208add1155SRico Sonntag     *
3218add1155SRico Sonntag     * @param int $threshold
3228add1155SRico Sonntag     * @param int $maxtoshow
3238add1155SRico Sonntag     *
3248add1155SRico Sonntag     * @return string
3258add1155SRico Sonntag     */
3268add1155SRico Sonntag    public function commonGivenMaleList(int $threshold = 1, int $maxtoshow = 10): string
3278add1155SRico Sonntag    {
3288add1155SRico Sonntag        return $this->commonGivenQuery('M', 'list', false, $threshold, $maxtoshow);
3298add1155SRico Sonntag    }
3308add1155SRico Sonntag
3318add1155SRico Sonntag    /**
3328add1155SRico Sonntag     * Find common give names of males.
3338add1155SRico Sonntag     *
3348add1155SRico Sonntag     * @param int $threshold
3358add1155SRico Sonntag     * @param int $maxtoshow
3368add1155SRico Sonntag     *
3378add1155SRico Sonntag     * @return string
3388add1155SRico Sonntag     */
3398add1155SRico Sonntag    public function commonGivenMaleListTotals(int $threshold = 1, int $maxtoshow = 10): string
3408add1155SRico Sonntag    {
3418add1155SRico Sonntag        return $this->commonGivenQuery('M', 'list', true, $threshold, $maxtoshow);
3428add1155SRico Sonntag    }
3438add1155SRico Sonntag
3448add1155SRico Sonntag    /**
3458add1155SRico Sonntag     * Find common give names of males.
3468add1155SRico Sonntag     *
3478add1155SRico Sonntag     * @param int $threshold
3488add1155SRico Sonntag     * @param int $maxtoshow
3498add1155SRico Sonntag     *
3508add1155SRico Sonntag     * @return string
3518add1155SRico Sonntag     */
3528add1155SRico Sonntag    public function commonGivenMaleTable(int $threshold = 1, int $maxtoshow = 10): string
3538add1155SRico Sonntag    {
3548add1155SRico Sonntag        return $this->commonGivenQuery('M', 'table', false, $threshold, $maxtoshow);
3558add1155SRico Sonntag    }
3568add1155SRico Sonntag
3578add1155SRico Sonntag    /**
3588add1155SRico Sonntag     * Find common give names of unknown sexes.
3598add1155SRico Sonntag     *
3608add1155SRico Sonntag     * @param int $threshold
3618add1155SRico Sonntag     * @param int $maxtoshow
3628add1155SRico Sonntag     *
3638add1155SRico Sonntag     * @return string
3648add1155SRico Sonntag     */
3658add1155SRico Sonntag    public function commonGivenUnknown(int $threshold = 1, int $maxtoshow = 10): string
3668add1155SRico Sonntag    {
3678add1155SRico Sonntag        return $this->commonGivenQuery('U', 'nolist', false, $threshold, $maxtoshow);
3688add1155SRico Sonntag    }
3698add1155SRico Sonntag
3708add1155SRico Sonntag    /**
3718add1155SRico Sonntag     * Find common give names of unknown sexes.
3728add1155SRico Sonntag     *
3738add1155SRico Sonntag     * @param int $threshold
3748add1155SRico Sonntag     * @param int $maxtoshow
3758add1155SRico Sonntag     *
3768add1155SRico Sonntag     * @return string
3778add1155SRico Sonntag     */
3788add1155SRico Sonntag    public function commonGivenUnknownTotals(int $threshold = 1, int $maxtoshow = 10): string
3798add1155SRico Sonntag    {
3808add1155SRico Sonntag        return $this->commonGivenQuery('U', 'nolist', true, $threshold, $maxtoshow);
3818add1155SRico Sonntag    }
3828add1155SRico Sonntag
3838add1155SRico Sonntag    /**
3848add1155SRico Sonntag     * Find common give names of unknown sexes.
3858add1155SRico Sonntag     *
3868add1155SRico Sonntag     * @param int $threshold
3878add1155SRico Sonntag     * @param int $maxtoshow
3888add1155SRico Sonntag     *
3898add1155SRico Sonntag     * @return string
3908add1155SRico Sonntag     */
3918add1155SRico Sonntag    public function commonGivenUnknownList(int $threshold = 1, int $maxtoshow = 10): string
3928add1155SRico Sonntag    {
3938add1155SRico Sonntag        return $this->commonGivenQuery('U', 'list', false, $threshold, $maxtoshow);
3948add1155SRico Sonntag    }
3958add1155SRico Sonntag
3968add1155SRico Sonntag    /**
3978add1155SRico Sonntag     * Find common give names of unknown sexes.
3988add1155SRico Sonntag     *
3998add1155SRico Sonntag     * @param int $threshold
4008add1155SRico Sonntag     * @param int $maxtoshow
4018add1155SRico Sonntag     *
4028add1155SRico Sonntag     * @return string
4038add1155SRico Sonntag     */
4048add1155SRico Sonntag    public function commonGivenUnknownListTotals(int $threshold = 1, int $maxtoshow = 10): string
4058add1155SRico Sonntag    {
4068add1155SRico Sonntag        return $this->commonGivenQuery('U', 'list', true, $threshold, $maxtoshow);
4078add1155SRico Sonntag    }
4088add1155SRico Sonntag
4098add1155SRico Sonntag    /**
4108add1155SRico Sonntag     * Find common give names of unknown sexes.
4118add1155SRico Sonntag     *
4128add1155SRico Sonntag     * @param int $threshold
4138add1155SRico Sonntag     * @param int $maxtoshow
4148add1155SRico Sonntag     *
4158add1155SRico Sonntag     * @return string
4168add1155SRico Sonntag     */
4178add1155SRico Sonntag    public function commonGivenUnknownTable(int $threshold = 1, int $maxtoshow = 10): string
4188add1155SRico Sonntag    {
4198add1155SRico Sonntag        return $this->commonGivenQuery('U', 'table', false, $threshold, $maxtoshow);
4208add1155SRico Sonntag    }
4218add1155SRico Sonntag
4228add1155SRico Sonntag    /**
4233dc8167dSGreg Roach     * Count the number of distinct given names (or the number of occurences of specific given names).
4248add1155SRico Sonntag     *
4253dc8167dSGreg Roach     * @param string[] ...$params
4268add1155SRico Sonntag     *
4278add1155SRico Sonntag     * @return string
4288add1155SRico Sonntag     */
4298add1155SRico Sonntag    public function totalGivennames(...$params): string
4308add1155SRico Sonntag    {
4313dc8167dSGreg Roach        $query = DB::table('name')
4323dc8167dSGreg Roach            ->where('n_file', '=', $this->tree->id());
4333dc8167dSGreg Roach
4343dc8167dSGreg Roach        if (empty($params)) {
435c09e99bfSRico Sonntag            // Count number of distinct given names.
4363dc8167dSGreg Roach            $query
437c09e99bfSRico Sonntag                ->distinct()
4382ccd1f0bSGreg Roach                ->where('n_givn', '<>', '@P.N.')
439c09e99bfSRico Sonntag                ->whereNotNull('n_givn');
4408add1155SRico Sonntag        } else {
441c09e99bfSRico Sonntag            // Count number of occurences of specific given names.
44244cdc21eSGreg Roach            $query->whereIn('n_givn', $params);
4438add1155SRico Sonntag        }
4448add1155SRico Sonntag
445c09e99bfSRico Sonntag        $count = $query->count('n_givn');
4463dc8167dSGreg Roach
4473dc8167dSGreg Roach        return I18N::number($count);
4488add1155SRico Sonntag    }
4498add1155SRico Sonntag
4508add1155SRico Sonntag    /**
451c09e99bfSRico Sonntag     * Count the number of distinct surnames (or the number of occurences of specific surnames).
4528add1155SRico Sonntag     *
4533dc8167dSGreg Roach     * @param string[] ...$params
4548add1155SRico Sonntag     *
4558add1155SRico Sonntag     * @return string
4568add1155SRico Sonntag     */
4578add1155SRico Sonntag    public function totalSurnames(...$params): string
4588add1155SRico Sonntag    {
4593dc8167dSGreg Roach        $query = DB::table('name')
4603dc8167dSGreg Roach            ->where('n_file', '=', $this->tree->id());
4613dc8167dSGreg Roach
4623dc8167dSGreg Roach        if (empty($params)) {
4633dc8167dSGreg Roach            // Count number of distinct surnames
464c09e99bfSRico Sonntag            $query->distinct()
465c09e99bfSRico Sonntag                ->whereNotNull('n_surn');
4668add1155SRico Sonntag        } else {
4673dc8167dSGreg Roach            // Count number of occurences of specific surnames.
4683dc8167dSGreg Roach            $query->whereIn('n_surn', $params);
4698add1155SRico Sonntag        }
4708add1155SRico Sonntag
471c09e99bfSRico Sonntag        $count = $query->count('n_surn');
4728add1155SRico Sonntag
4733dc8167dSGreg Roach        return I18N::number($count);
4748add1155SRico Sonntag    }
4758add1155SRico Sonntag
4768add1155SRico Sonntag    /**
4778add1155SRico Sonntag     * @param int $number_of_surnames
4788add1155SRico Sonntag     * @param int $threshold
4798add1155SRico Sonntag     *
4808add1155SRico Sonntag     * @return \stdClass[]
4818add1155SRico Sonntag     */
4828add1155SRico Sonntag    private function topSurnames(int $number_of_surnames, int $threshold): array
4838add1155SRico Sonntag    {
4848add1155SRico Sonntag        // Use the count of base surnames.
485d1a467e4SGreg Roach        $top_surnames = DB::table('name')
486d1a467e4SGreg Roach            ->where('n_file', '=', $this->tree->id())
487d1a467e4SGreg Roach            ->where('n_type', '<>', '_MARNM')
488d1a467e4SGreg Roach            ->whereNotIn('n_surn', ['', '@N.N.'])
489d1a467e4SGreg Roach            ->select('n_surn')
490d1a467e4SGreg Roach            ->groupBy('n_surn')
4913413ec75SRico Sonntag            ->orderByRaw('count(n_surn) desc')
492d1a467e4SGreg Roach            ->take($number_of_surnames)
493d1a467e4SGreg Roach            ->get()
494d1a467e4SGreg Roach            ->pluck('n_surn')
495d1a467e4SGreg Roach            ->all();
4968add1155SRico Sonntag
4978add1155SRico Sonntag        $surnames = [];
4988add1155SRico Sonntag        foreach ($top_surnames as $top_surname) {
499d1a467e4SGreg Roach            $variants = DB::table('name')
500d1a467e4SGreg Roach                ->where('n_file', '=', $this->tree->id())
501d1a467e4SGreg Roach                ->where(DB::raw('n_surn /* COLLATE ' . I18N::collation() . ' */'), '=', $top_surname)
502d1a467e4SGreg Roach                ->select('n_surn', DB::raw('COUNT(*) AS count'))
503d1a467e4SGreg Roach                ->groupBy('n_surn')
504d1a467e4SGreg Roach                ->get()
505d1a467e4SGreg Roach                ->pluck('count', 'n_surn')
506d1a467e4SGreg Roach                ->all();
5078add1155SRico Sonntag
5088add1155SRico Sonntag            if (array_sum($variants) > $threshold) {
5098add1155SRico Sonntag                $surnames[$top_surname] = $variants;
5108add1155SRico Sonntag            }
5118add1155SRico Sonntag        }
5128add1155SRico Sonntag
5138add1155SRico Sonntag        return $surnames;
5148add1155SRico Sonntag    }
5158add1155SRico Sonntag
5168add1155SRico Sonntag    /**
5178add1155SRico Sonntag     * Find common surnames.
5188add1155SRico Sonntag     *
5198add1155SRico Sonntag     * @return string
5208add1155SRico Sonntag     */
5218add1155SRico Sonntag    public function getCommonSurname(): string
5228add1155SRico Sonntag    {
5238add1155SRico Sonntag        $top_surname = $this->topSurnames(1, 0);
524f24db0ceSRico Sonntag
525f24db0ceSRico Sonntag        return $top_surname
526f24db0ceSRico Sonntag            ? implode(', ', array_keys(array_shift($top_surname)) ?? [])
527f24db0ceSRico Sonntag            : '';
5288add1155SRico Sonntag    }
5298add1155SRico Sonntag
5308add1155SRico Sonntag    /**
5318add1155SRico Sonntag     * Find common surnames.
5328add1155SRico Sonntag     *
5338add1155SRico Sonntag     * @param string $type
5348add1155SRico Sonntag     * @param bool   $show_tot
5358add1155SRico Sonntag     * @param int    $threshold
5368add1155SRico Sonntag     * @param int    $number_of_surnames
5378add1155SRico Sonntag     * @param string $sorting
5388add1155SRico Sonntag     *
5398add1155SRico Sonntag     * @return string
5408add1155SRico Sonntag     */
5418add1155SRico Sonntag    private function commonSurnamesQuery(
5428add1155SRico Sonntag        string $type,
5438add1155SRico Sonntag        bool $show_tot,
5448add1155SRico Sonntag        int $threshold,
5458add1155SRico Sonntag        int $number_of_surnames,
5468add1155SRico Sonntag        string $sorting
5478add1155SRico Sonntag    ): string {
5488add1155SRico Sonntag        $surnames = $this->topSurnames($number_of_surnames, $threshold);
5498add1155SRico Sonntag
5508add1155SRico Sonntag        switch ($sorting) {
5518add1155SRico Sonntag            default:
5528add1155SRico Sonntag            case 'alpha':
5538add1155SRico Sonntag                uksort($surnames, [I18N::class, 'strcasecmp']);
5548add1155SRico Sonntag                break;
5558add1155SRico Sonntag            case 'count':
5568add1155SRico Sonntag                break;
5578add1155SRico Sonntag            case 'rcount':
5588add1155SRico Sonntag                $surnames = array_reverse($surnames, true);
5598add1155SRico Sonntag                break;
5608add1155SRico Sonntag        }
5618add1155SRico Sonntag
56267992b6aSRichard Cissee        //find a module providing individual lists
56387cca37cSGreg Roach        $module = app(ModuleService::class)->findByComponent(ModuleListInterface::class, $this->tree, Auth::user())->first(function (ModuleInterface $module) {
56467992b6aSRichard Cissee            return $module instanceof IndividualListModule;
56567992b6aSRichard Cissee        });
56667992b6aSRichard Cissee
5678add1155SRico Sonntag        return FunctionsPrintLists::surnameList(
5688add1155SRico Sonntag            $surnames,
5698add1155SRico Sonntag            ($type === 'list' ? 1 : 2),
5708add1155SRico Sonntag            $show_tot,
57167992b6aSRichard Cissee            $module,
5728add1155SRico Sonntag            $this->tree
5738add1155SRico Sonntag        );
5748add1155SRico Sonntag    }
5758add1155SRico Sonntag
5768add1155SRico Sonntag    /**
5778add1155SRico Sonntag     * Find common surnames.
5788add1155SRico Sonntag     *
5798add1155SRico Sonntag     * @param int    $threshold
5808add1155SRico Sonntag     * @param int    $number_of_surnames
5818add1155SRico Sonntag     * @param string $sorting
5828add1155SRico Sonntag     *
5838add1155SRico Sonntag     * @return string
5848add1155SRico Sonntag     */
5858add1155SRico Sonntag    public function commonSurnames(
5868add1155SRico Sonntag        int $threshold = 1,
5878add1155SRico Sonntag        int $number_of_surnames = 10,
5888add1155SRico Sonntag        string $sorting = 'alpha'
5898add1155SRico Sonntag    ): string {
5908add1155SRico Sonntag        return $this->commonSurnamesQuery('nolist', false, $threshold, $number_of_surnames, $sorting);
5918add1155SRico Sonntag    }
5928add1155SRico Sonntag
5938add1155SRico Sonntag    /**
5948add1155SRico Sonntag     * Find common surnames.
5958add1155SRico Sonntag     *
5968add1155SRico Sonntag     * @param int    $threshold
5978add1155SRico Sonntag     * @param int    $number_of_surnames
5988add1155SRico Sonntag     * @param string $sorting
5998add1155SRico Sonntag     *
6008add1155SRico Sonntag     * @return string
6018add1155SRico Sonntag     */
6028add1155SRico Sonntag    public function commonSurnamesTotals(
6038add1155SRico Sonntag        int $threshold = 1,
6048add1155SRico Sonntag        int $number_of_surnames = 10,
6058add1155SRico Sonntag        string $sorting = 'rcount'
6068add1155SRico Sonntag    ): string {
6078add1155SRico Sonntag        return $this->commonSurnamesQuery('nolist', true, $threshold, $number_of_surnames, $sorting);
6088add1155SRico Sonntag    }
6098add1155SRico Sonntag
6108add1155SRico Sonntag    /**
6118add1155SRico Sonntag     * Find common surnames.
6128add1155SRico Sonntag     *
6138add1155SRico Sonntag     * @param int    $threshold
6148add1155SRico Sonntag     * @param int    $number_of_surnames
6158add1155SRico Sonntag     * @param string $sorting
6168add1155SRico Sonntag     *
6178add1155SRico Sonntag     * @return string
6188add1155SRico Sonntag     */
6198add1155SRico Sonntag    public function commonSurnamesList(
6208add1155SRico Sonntag        int $threshold = 1,
6218add1155SRico Sonntag        int $number_of_surnames = 10,
6228add1155SRico Sonntag        string $sorting = 'alpha'
6238add1155SRico Sonntag    ): string {
6248add1155SRico Sonntag        return $this->commonSurnamesQuery('list', false, $threshold, $number_of_surnames, $sorting);
6258add1155SRico Sonntag    }
6268add1155SRico Sonntag
6278add1155SRico Sonntag    /**
6288add1155SRico Sonntag     * Find common surnames.
6298add1155SRico Sonntag     *
6308add1155SRico Sonntag     * @param int    $threshold
6318add1155SRico Sonntag     * @param int    $number_of_surnames
6328add1155SRico Sonntag     * @param string $sorting
6338add1155SRico Sonntag     *
6348add1155SRico Sonntag     * @return string
6358add1155SRico Sonntag     */
6368add1155SRico Sonntag    public function commonSurnamesListTotals(
6378add1155SRico Sonntag        int $threshold = 1,
6388add1155SRico Sonntag        int $number_of_surnames = 10,
6398add1155SRico Sonntag        string $sorting = 'rcount'
6408add1155SRico Sonntag    ): string {
6418add1155SRico Sonntag        return $this->commonSurnamesQuery('list', true, $threshold, $number_of_surnames, $sorting);
6428add1155SRico Sonntag    }
6438add1155SRico Sonntag
6448add1155SRico Sonntag    /**
645cde1d378SGreg Roach     * Get a count of births by month.
6468add1155SRico Sonntag     *
6478add1155SRico Sonntag     * @param int  $year1
6488add1155SRico Sonntag     * @param int  $year2
6498add1155SRico Sonntag     *
650cde1d378SGreg Roach     * @return Builder
6518add1155SRico Sonntag     */
652cde1d378SGreg Roach    public function statsBirthQuery(int $year1 = -1, int $year2 = -1): Builder
6538add1155SRico Sonntag    {
654d1a467e4SGreg Roach        $query = DB::table('dates')
6553413ec75SRico Sonntag            ->select(['d_month', DB::raw('COUNT(*) AS total')])
656d1a467e4SGreg Roach            ->where('d_file', '=', $this->tree->id())
657d1a467e4SGreg Roach            ->where('d_fact', '=', 'BIRT')
658d1a467e4SGreg Roach            ->whereIn('d_type', ['@#DGREGORIAN@', '@#DJULIAN@'])
659d1a467e4SGreg Roach            ->groupBy('d_month');
6608add1155SRico Sonntag
6618add1155SRico Sonntag        if ($year1 >= 0 && $year2 >= 0) {
662d1a467e4SGreg Roach            $query->whereBetween('d_year', [$year1, $year2]);
6638add1155SRico Sonntag        }
6648add1155SRico Sonntag
665cde1d378SGreg Roach        return $query;
666cde1d378SGreg Roach    }
667cde1d378SGreg Roach
668cde1d378SGreg Roach    /**
669cde1d378SGreg Roach     * Get a count of births by month.
670cde1d378SGreg Roach     *
671cde1d378SGreg Roach     * @param int  $year1
672cde1d378SGreg Roach     * @param int  $year2
673cde1d378SGreg Roach     *
674cde1d378SGreg Roach     * @return Builder
675cde1d378SGreg Roach     */
676cde1d378SGreg Roach    public function statsBirthBySexQuery(int $year1 = -1, int $year2 = -1): Builder
677cde1d378SGreg Roach    {
678cde1d378SGreg Roach        return $this->statsBirthQuery($year1, $year2)
6793413ec75SRico Sonntag                ->select(['d_month', 'i_sex', DB::raw('COUNT(*) AS total')])
680d1a467e4SGreg Roach                ->join('individuals', function (JoinClause $join): void {
681d1a467e4SGreg Roach                    $join
682d1a467e4SGreg Roach                        ->on('i_id', '=', 'd_gid')
683d1a467e4SGreg Roach                        ->on('i_file', '=', 'd_file');
684d1a467e4SGreg Roach                })
6853413ec75SRico Sonntag                ->groupBy('i_sex');
6868add1155SRico Sonntag    }
6878add1155SRico Sonntag
6888add1155SRico Sonntag    /**
6898add1155SRico Sonntag     * General query on births.
6908add1155SRico Sonntag     *
6918add1155SRico Sonntag     * @param string|null $color_from
6928add1155SRico Sonntag     * @param string|null $color_to
6938add1155SRico Sonntag     *
6948add1155SRico Sonntag     * @return string
6958add1155SRico Sonntag     */
69688de55fdSRico Sonntag    public function statsBirth(string $color_from = null, string $color_to = null): string
6978add1155SRico Sonntag    {
6988add1155SRico Sonntag        return (new ChartBirth($this->tree))
69988de55fdSRico Sonntag            ->chartBirth($color_from, $color_to);
7008add1155SRico Sonntag    }
7018add1155SRico Sonntag
7028add1155SRico Sonntag    /**
7038add1155SRico Sonntag     * Get a list of death dates.
7048add1155SRico Sonntag     *
7058add1155SRico Sonntag     * @param int  $year1
7068add1155SRico Sonntag     * @param int  $year2
7078add1155SRico Sonntag     *
708cde1d378SGreg Roach     * @return Builder
7098add1155SRico Sonntag     */
710cde1d378SGreg Roach    public function statsDeathQuery(int $year1 = -1, int $year2 = -1): Builder
7118add1155SRico Sonntag    {
712d1a467e4SGreg Roach        $query = DB::table('dates')
7133413ec75SRico Sonntag            ->select(['d_month', DB::raw('COUNT(*) AS total')])
714d1a467e4SGreg Roach            ->where('d_file', '=', $this->tree->id())
715d1a467e4SGreg Roach            ->where('d_fact', '=', 'DEAT')
716d1a467e4SGreg Roach            ->whereIn('d_type', ['@#DGREGORIAN@', '@#DJULIAN@'])
717d1a467e4SGreg Roach            ->groupBy('d_month');
7188add1155SRico Sonntag
7198add1155SRico Sonntag        if ($year1 >= 0 && $year2 >= 0) {
720d1a467e4SGreg Roach            $query->whereBetween('d_year', [$year1, $year2]);
7218add1155SRico Sonntag        }
7228add1155SRico Sonntag
723cde1d378SGreg Roach        return $query;
724cde1d378SGreg Roach    }
725cde1d378SGreg Roach
726cde1d378SGreg Roach    /**
727cde1d378SGreg Roach     * Get a list of death dates.
728cde1d378SGreg Roach     *
729cde1d378SGreg Roach     * @param int  $year1
730cde1d378SGreg Roach     * @param int  $year2
731cde1d378SGreg Roach     *
732cde1d378SGreg Roach     * @return Builder
733cde1d378SGreg Roach     */
734cde1d378SGreg Roach    public function statsDeathBySexQuery(int $year1 = -1, int $year2 = -1): Builder
735cde1d378SGreg Roach    {
736cde1d378SGreg Roach        return $this->statsDeathQuery($year1, $year2)
7373413ec75SRico Sonntag                ->select(['d_month', 'i_sex', DB::raw('COUNT(*) AS total')])
738d1a467e4SGreg Roach                ->join('individuals', function (JoinClause $join): void {
739d1a467e4SGreg Roach                    $join
740d1a467e4SGreg Roach                        ->on('i_id', '=', 'd_gid')
741d1a467e4SGreg Roach                        ->on('i_file', '=', 'd_file');
742d1a467e4SGreg Roach                })
7433413ec75SRico Sonntag                ->groupBy('i_sex');
7448add1155SRico Sonntag    }
7458add1155SRico Sonntag
7468add1155SRico Sonntag    /**
7478add1155SRico Sonntag     * General query on deaths.
7488add1155SRico Sonntag     *
7498add1155SRico Sonntag     * @param string|null $color_from
7508add1155SRico Sonntag     * @param string|null $color_to
7518add1155SRico Sonntag     *
7528add1155SRico Sonntag     * @return string
7538add1155SRico Sonntag     */
75488de55fdSRico Sonntag    public function statsDeath(string $color_from = null, string $color_to = null): string
7558add1155SRico Sonntag    {
7568add1155SRico Sonntag        return (new ChartDeath($this->tree))
75788de55fdSRico Sonntag            ->chartDeath($color_from, $color_to);
7588add1155SRico Sonntag    }
7598add1155SRico Sonntag
7608add1155SRico Sonntag    /**
7618add1155SRico Sonntag     * General query on ages.
7628add1155SRico Sonntag     *
7638add1155SRico Sonntag     * @param string $related
7648add1155SRico Sonntag     * @param string $sex
7658add1155SRico Sonntag     * @param int    $year1
7668add1155SRico Sonntag     * @param int    $year2
7678add1155SRico Sonntag     *
7688add1155SRico Sonntag     * @return array|string
7698add1155SRico Sonntag     */
7708add1155SRico Sonntag    public function statsAgeQuery(string $related = 'BIRT', string $sex = 'BOTH', int $year1 = -1, int $year2 = -1)
7718add1155SRico Sonntag    {
77244cdc21eSGreg Roach        $prefix = DB::connection()->getTablePrefix();
7738add1155SRico Sonntag
77444cdc21eSGreg Roach        $query = $this->birthAndDeathQuery($sex);
7758add1155SRico Sonntag
7768add1155SRico Sonntag        if ($year1 >= 0 && $year2 >= 0) {
77744cdc21eSGreg Roach            $query
77844cdc21eSGreg Roach                ->whereIn('birth.d_type', ['@#DGREGORIAN@', '@#DJULIAN@'])
77944cdc21eSGreg Roach                ->whereIn('death.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']);
78044cdc21eSGreg Roach
7818add1155SRico Sonntag            if ($related === 'BIRT') {
78244cdc21eSGreg Roach                $query->whereBetween('birth.d_year', [$year1, $year2]);
7838add1155SRico Sonntag            } elseif ($related === 'DEAT') {
78444cdc21eSGreg Roach                $query->whereBetween('death.d_year', [$year1, $year2]);
7858add1155SRico Sonntag            }
7868add1155SRico Sonntag        }
7878add1155SRico Sonntag
78844cdc21eSGreg Roach        return $query
78944cdc21eSGreg Roach            ->select(DB::raw($prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1 AS days'))
790c8c87812SRico Sonntag            ->orderBy('days', 'desc')
79144cdc21eSGreg Roach            ->get()
79244cdc21eSGreg Roach            ->all();
7938add1155SRico Sonntag    }
7948add1155SRico Sonntag
7958add1155SRico Sonntag    /**
7968add1155SRico Sonntag     * General query on ages.
7978add1155SRico Sonntag     *
7988add1155SRico Sonntag     * @return string
7998add1155SRico Sonntag     */
80088de55fdSRico Sonntag    public function statsAge(): string
8018add1155SRico Sonntag    {
80288de55fdSRico Sonntag        return (new ChartAge($this->tree))->chartAge();
8038add1155SRico Sonntag    }
8048add1155SRico Sonntag
8058add1155SRico Sonntag    /**
8068add1155SRico Sonntag     * Lifespan
8078add1155SRico Sonntag     *
8088add1155SRico Sonntag     * @param string $type
8098add1155SRico Sonntag     * @param string $sex
8108add1155SRico Sonntag     *
8118add1155SRico Sonntag     * @return string
8128add1155SRico Sonntag     */
8138add1155SRico Sonntag    private function longlifeQuery(string $type, string $sex): string
8148add1155SRico Sonntag    {
81544cdc21eSGreg Roach        $prefix = DB::connection()->getTablePrefix();
8168add1155SRico Sonntag
81744cdc21eSGreg Roach        $row = $this->birthAndDeathQuery($sex)
81844cdc21eSGreg Roach            ->orderBy('days', 'desc')
81944cdc21eSGreg Roach            ->select(['individuals.*', DB::raw($prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1 AS days')])
82044cdc21eSGreg Roach            ->first();
82144cdc21eSGreg Roach
82244cdc21eSGreg Roach        if ($row === null) {
8238add1155SRico Sonntag            return '';
8248add1155SRico Sonntag        }
82544cdc21eSGreg Roach
82644cdc21eSGreg Roach        /** @var Individual $individual */
82744cdc21eSGreg Roach        $individual = Individual::rowMapper()($row);
82844cdc21eSGreg Roach
82944cdc21eSGreg Roach        if (!$individual->canShow()) {
83044cdc21eSGreg Roach            return I18N::translate('This information is private and cannot be shown.');
83144cdc21eSGreg Roach        }
83244cdc21eSGreg Roach
8338add1155SRico Sonntag        switch ($type) {
8348add1155SRico Sonntag            default:
8358add1155SRico Sonntag            case 'full':
83644cdc21eSGreg Roach                return $individual->formatList();
8378add1155SRico Sonntag
83844cdc21eSGreg Roach            case 'age':
83944cdc21eSGreg Roach                return I18N::number((int) ($row->days / 365.25));
84044cdc21eSGreg Roach
84144cdc21eSGreg Roach            case 'name':
84239ca88baSGreg Roach                return '<a href="' . e($individual->url()) . '">' . $individual->fullName() . '</a>';
84344cdc21eSGreg Roach        }
8448add1155SRico Sonntag    }
8458add1155SRico Sonntag
8468add1155SRico Sonntag    /**
8478add1155SRico Sonntag     * Find the longest lived individual.
8488add1155SRico Sonntag     *
8498add1155SRico Sonntag     * @return string
8508add1155SRico Sonntag     */
8518add1155SRico Sonntag    public function longestLife(): string
8528add1155SRico Sonntag    {
8538add1155SRico Sonntag        return $this->longlifeQuery('full', 'BOTH');
8548add1155SRico Sonntag    }
8558add1155SRico Sonntag
8568add1155SRico Sonntag    /**
8578add1155SRico Sonntag     * Find the age of the longest lived individual.
8588add1155SRico Sonntag     *
8598add1155SRico Sonntag     * @return string
8608add1155SRico Sonntag     */
8618add1155SRico Sonntag    public function longestLifeAge(): string
8628add1155SRico Sonntag    {
8638add1155SRico Sonntag        return $this->longlifeQuery('age', 'BOTH');
8648add1155SRico Sonntag    }
8658add1155SRico Sonntag
8668add1155SRico Sonntag    /**
8678add1155SRico Sonntag     * Find the name of the longest lived individual.
8688add1155SRico Sonntag     *
8698add1155SRico Sonntag     * @return string
8708add1155SRico Sonntag     */
8718add1155SRico Sonntag    public function longestLifeName(): string
8728add1155SRico Sonntag    {
8738add1155SRico Sonntag        return $this->longlifeQuery('name', 'BOTH');
8748add1155SRico Sonntag    }
8758add1155SRico Sonntag
8768add1155SRico Sonntag    /**
8778add1155SRico Sonntag     * Find the longest lived female.
8788add1155SRico Sonntag     *
8798add1155SRico Sonntag     * @return string
8808add1155SRico Sonntag     */
8818add1155SRico Sonntag    public function longestLifeFemale(): string
8828add1155SRico Sonntag    {
8838add1155SRico Sonntag        return $this->longlifeQuery('full', 'F');
8848add1155SRico Sonntag    }
8858add1155SRico Sonntag
8868add1155SRico Sonntag    /**
8878add1155SRico Sonntag     * Find the age of the longest lived female.
8888add1155SRico Sonntag     *
8898add1155SRico Sonntag     * @return string
8908add1155SRico Sonntag     */
8918add1155SRico Sonntag    public function longestLifeFemaleAge(): string
8928add1155SRico Sonntag    {
8938add1155SRico Sonntag        return $this->longlifeQuery('age', 'F');
8948add1155SRico Sonntag    }
8958add1155SRico Sonntag
8968add1155SRico Sonntag    /**
8978add1155SRico Sonntag     * Find the name of the longest lived female.
8988add1155SRico Sonntag     *
8998add1155SRico Sonntag     * @return string
9008add1155SRico Sonntag     */
9018add1155SRico Sonntag    public function longestLifeFemaleName(): string
9028add1155SRico Sonntag    {
9038add1155SRico Sonntag        return $this->longlifeQuery('name', 'F');
9048add1155SRico Sonntag    }
9058add1155SRico Sonntag
9068add1155SRico Sonntag    /**
9078add1155SRico Sonntag     * Find the longest lived male.
9088add1155SRico Sonntag     *
9098add1155SRico Sonntag     * @return string
9108add1155SRico Sonntag     */
9118add1155SRico Sonntag    public function longestLifeMale(): string
9128add1155SRico Sonntag    {
9138add1155SRico Sonntag        return $this->longlifeQuery('full', 'M');
9148add1155SRico Sonntag    }
9158add1155SRico Sonntag
9168add1155SRico Sonntag    /**
9178add1155SRico Sonntag     * Find the age of the longest lived male.
9188add1155SRico Sonntag     *
9198add1155SRico Sonntag     * @return string
9208add1155SRico Sonntag     */
9218add1155SRico Sonntag    public function longestLifeMaleAge(): string
9228add1155SRico Sonntag    {
9238add1155SRico Sonntag        return $this->longlifeQuery('age', 'M');
9248add1155SRico Sonntag    }
9258add1155SRico Sonntag
9268add1155SRico Sonntag    /**
9278add1155SRico Sonntag     * Find the name of the longest lived male.
9288add1155SRico Sonntag     *
9298add1155SRico Sonntag     * @return string
9308add1155SRico Sonntag     */
9318add1155SRico Sonntag    public function longestLifeMaleName(): string
9328add1155SRico Sonntag    {
9338add1155SRico Sonntag        return $this->longlifeQuery('name', 'M');
9348add1155SRico Sonntag    }
9358add1155SRico Sonntag
9368add1155SRico Sonntag    /**
9378add1155SRico Sonntag     * Returns the calculated age the time of event.
9388add1155SRico Sonntag     *
9398add1155SRico Sonntag     * @param int $age The age from the database record
9408add1155SRico Sonntag     *
9418add1155SRico Sonntag     * @return string
9428add1155SRico Sonntag     */
9438add1155SRico Sonntag    private function calculateAge(int $age): string
9448add1155SRico Sonntag    {
9458add1155SRico Sonntag        if ((int) ($age / 365.25) > 0) {
9468add1155SRico Sonntag            $result = (int) ($age / 365.25) . 'y';
9478add1155SRico Sonntag        } elseif ((int) ($age / 30.4375) > 0) {
9488add1155SRico Sonntag            $result = (int) ($age / 30.4375) . 'm';
9498add1155SRico Sonntag        } else {
9508add1155SRico Sonntag            $result = $age . 'd';
9518add1155SRico Sonntag        }
9528add1155SRico Sonntag
9538add1155SRico Sonntag        return FunctionsDate::getAgeAtEvent($result);
9548add1155SRico Sonntag    }
9558add1155SRico Sonntag
9568add1155SRico Sonntag    /**
9578add1155SRico Sonntag     * Find the oldest individuals.
9588add1155SRico Sonntag     *
9598add1155SRico Sonntag     * @param string $sex
9608add1155SRico Sonntag     * @param int    $total
9618add1155SRico Sonntag     *
9628add1155SRico Sonntag     * @return array
9638add1155SRico Sonntag     */
9648add1155SRico Sonntag    private function topTenOldestQuery(string $sex, int $total): array
9658add1155SRico Sonntag    {
96644cdc21eSGreg Roach        $prefix = DB::connection()->getTablePrefix();
9678add1155SRico Sonntag
96844cdc21eSGreg Roach        $rows = $this->birthAndDeathQuery($sex)
96944cdc21eSGreg Roach            ->groupBy(['i_id', 'i_file'])
97044cdc21eSGreg Roach            ->orderBy('days', 'desc')
97144cdc21eSGreg Roach            ->select(['individuals.*', DB::raw('MAX(' . $prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1) AS days')])
97244cdc21eSGreg Roach            ->take($total)
97344cdc21eSGreg Roach            ->get();
9748add1155SRico Sonntag
9758add1155SRico Sonntag        $top10 = [];
9768add1155SRico Sonntag        foreach ($rows as $row) {
97744cdc21eSGreg Roach            /** @var Individual $individual */
97844cdc21eSGreg Roach            $individual = Individual::rowMapper()($row);
9798add1155SRico Sonntag
98044cdc21eSGreg Roach            if ($individual->canShow()) {
9818add1155SRico Sonntag                $top10[] = [
98244cdc21eSGreg Roach                    'person' => $individual,
98344cdc21eSGreg Roach                    'age'    => $this->calculateAge((int) $row->days),
9848add1155SRico Sonntag                ];
9858add1155SRico Sonntag            }
9868add1155SRico Sonntag        }
9878add1155SRico Sonntag
9888add1155SRico Sonntag        return $top10;
9898add1155SRico Sonntag    }
9908add1155SRico Sonntag
9918add1155SRico Sonntag    /**
9928add1155SRico Sonntag     * Find the oldest individuals.
9938add1155SRico Sonntag     *
9948add1155SRico Sonntag     * @param int $total
9958add1155SRico Sonntag     *
9968add1155SRico Sonntag     * @return string
9978add1155SRico Sonntag     */
9988add1155SRico Sonntag    public function topTenOldest(int $total = 10): string
9998add1155SRico Sonntag    {
10008add1155SRico Sonntag        $records = $this->topTenOldestQuery('BOTH', $total);
10018add1155SRico Sonntag
1002c0112ce8SGreg Roach        return view('statistics/individuals/top10-nolist', [
10038add1155SRico Sonntag            'records' => $records,
1004c0112ce8SGreg Roach        ]);
10058add1155SRico Sonntag    }
10068add1155SRico Sonntag
10078add1155SRico Sonntag    /**
10088add1155SRico Sonntag     * Find the oldest living individuals.
10098add1155SRico Sonntag     *
10108add1155SRico Sonntag     * @param int $total
10118add1155SRico Sonntag     *
10128add1155SRico Sonntag     * @return string
10138add1155SRico Sonntag     */
10148add1155SRico Sonntag    public function topTenOldestList(int $total = 10): string
10158add1155SRico Sonntag    {
10168add1155SRico Sonntag        $records = $this->topTenOldestQuery('BOTH', $total);
10178add1155SRico Sonntag
1018c0112ce8SGreg Roach        return view('statistics/individuals/top10-list', [
10198add1155SRico Sonntag            'records' => $records,
1020c0112ce8SGreg Roach        ]);
10218add1155SRico Sonntag    }
10228add1155SRico Sonntag
10238add1155SRico Sonntag    /**
10248add1155SRico Sonntag     * Find the oldest females.
10258add1155SRico Sonntag     *
10268add1155SRico Sonntag     * @param int $total
10278add1155SRico Sonntag     *
10288add1155SRico Sonntag     * @return string
10298add1155SRico Sonntag     */
10308add1155SRico Sonntag    public function topTenOldestFemale(int $total = 10): string
10318add1155SRico Sonntag    {
10328add1155SRico Sonntag        $records = $this->topTenOldestQuery('F', $total);
10338add1155SRico Sonntag
1034c0112ce8SGreg Roach        return view('statistics/individuals/top10-nolist', [
10358add1155SRico Sonntag            'records' => $records,
1036c0112ce8SGreg Roach        ]);
10378add1155SRico Sonntag    }
10388add1155SRico Sonntag
10398add1155SRico Sonntag    /**
10408add1155SRico Sonntag     * Find the oldest living females.
10418add1155SRico Sonntag     *
10428add1155SRico Sonntag     * @param int $total
10438add1155SRico Sonntag     *
10448add1155SRico Sonntag     * @return string
10458add1155SRico Sonntag     */
10468add1155SRico Sonntag    public function topTenOldestFemaleList(int $total = 10): string
10478add1155SRico Sonntag    {
10488add1155SRico Sonntag        $records = $this->topTenOldestQuery('F', $total);
10498add1155SRico Sonntag
1050c0112ce8SGreg Roach        return view('statistics/individuals/top10-list', [
10518add1155SRico Sonntag            'records' => $records,
1052c0112ce8SGreg Roach        ]);
10538add1155SRico Sonntag    }
10548add1155SRico Sonntag
10558add1155SRico Sonntag    /**
10568add1155SRico Sonntag     * Find the longest lived males.
10578add1155SRico Sonntag     *
10588add1155SRico Sonntag     * @param int $total
10598add1155SRico Sonntag     *
10608add1155SRico Sonntag     * @return string
10618add1155SRico Sonntag     */
10628add1155SRico Sonntag    public function topTenOldestMale(int $total = 10): string
10638add1155SRico Sonntag    {
10648add1155SRico Sonntag        $records = $this->topTenOldestQuery('M', $total);
10658add1155SRico Sonntag
1066c0112ce8SGreg Roach        return view('statistics/individuals/top10-nolist', [
10678add1155SRico Sonntag            'records' => $records,
1068c0112ce8SGreg Roach        ]);
10698add1155SRico Sonntag    }
10708add1155SRico Sonntag
10718add1155SRico Sonntag    /**
10728add1155SRico Sonntag     * Find the longest lived males.
10738add1155SRico Sonntag     *
10748add1155SRico Sonntag     * @param int $total
10758add1155SRico Sonntag     *
10768add1155SRico Sonntag     * @return string
10778add1155SRico Sonntag     */
10788add1155SRico Sonntag    public function topTenOldestMaleList(int $total = 10): string
10798add1155SRico Sonntag    {
10808add1155SRico Sonntag        $records = $this->topTenOldestQuery('M', $total);
10818add1155SRico Sonntag
1082c0112ce8SGreg Roach        return view('statistics/individuals/top10-list', [
10838add1155SRico Sonntag            'records' => $records,
1084c0112ce8SGreg Roach        ]);
10858add1155SRico Sonntag    }
10868add1155SRico Sonntag
10878add1155SRico Sonntag    /**
10888add1155SRico Sonntag     * Find the oldest living individuals.
10898add1155SRico Sonntag     *
1090c0112ce8SGreg Roach     * @param string $sex   "M", "F" or "BOTH"
10918add1155SRico Sonntag     * @param int    $total
10928add1155SRico Sonntag     *
10938add1155SRico Sonntag     * @return array
10948add1155SRico Sonntag     */
1095c0112ce8SGreg Roach    private function topTenOldestAliveQuery(string $sex, int $total): array
10968add1155SRico Sonntag    {
1097d1a467e4SGreg Roach        $query = DB::table('dates')
1098d1a467e4SGreg Roach            ->join('individuals', function (JoinClause $join): void {
1099d1a467e4SGreg Roach                $join
1100d1a467e4SGreg Roach                    ->on('i_id', '=', 'd_gid')
1101d1a467e4SGreg Roach                    ->on('i_file', '=', 'd_file');
1102d1a467e4SGreg Roach            })
1103d1a467e4SGreg Roach            ->where('d_file', '=', $this->tree->id())
1104d1a467e4SGreg Roach            ->where('d_julianday1', '<>', 0)
1105d1a467e4SGreg Roach            ->where('d_fact', '=', 'BIRT')
1106d1a467e4SGreg Roach            ->where('i_gedcom', 'NOT LIKE', "%\n1 DEAT%")
1107d1a467e4SGreg Roach            ->where('i_gedcom', 'NOT LIKE', "%\n1 BURI%")
1108d1a467e4SGreg Roach            ->where('i_gedcom', 'NOT LIKE', "%\n1 CREM%");
1109d1a467e4SGreg Roach
1110d1a467e4SGreg Roach        if ($sex === 'F' || $sex === 'M') {
1111d1a467e4SGreg Roach            $query->where('i_sex', '=', $sex);
11128add1155SRico Sonntag        }
11138add1155SRico Sonntag
1114c0112ce8SGreg Roach        return $query
1115d1a467e4SGreg Roach            ->groupBy(['i_id', 'i_file'])
1116d1a467e4SGreg Roach            ->orderBy(DB::raw('MIN(d_julianday1)'))
1117d1a467e4SGreg Roach            ->select('individuals.*')
1118d1a467e4SGreg Roach            ->take($total)
1119d1a467e4SGreg Roach            ->get()
1120d1a467e4SGreg Roach            ->map(Individual::rowMapper())
1121c0112ce8SGreg Roach            ->filter(GedcomRecord::accessFilter())
1122c0112ce8SGreg Roach            ->map(function (Individual $individual): array {
1123d1a467e4SGreg Roach                $birth_jd = $individual->getBirthDate()->minimumJulianDay();
11248add1155SRico Sonntag
1125c0112ce8SGreg Roach                return [
1126d1a467e4SGreg Roach                    'person' => $individual,
1127*269fd10dSGreg Roach                    'age'    => $this->calculateAge(unixtojd(Carbon::now()->timestamp) - $birth_jd),
11288add1155SRico Sonntag                ];
1129c0112ce8SGreg Roach            })
1130c0112ce8SGreg Roach            ->all();
11318add1155SRico Sonntag    }
11328add1155SRico Sonntag
11338add1155SRico Sonntag    /**
11348add1155SRico Sonntag     * Find the oldest living individuals.
11358add1155SRico Sonntag     *
11368add1155SRico Sonntag     * @param int $total
11378add1155SRico Sonntag     *
11388add1155SRico Sonntag     * @return string
11398add1155SRico Sonntag     */
11408add1155SRico Sonntag    public function topTenOldestAlive(int $total = 10): string
11418add1155SRico Sonntag    {
11428add1155SRico Sonntag        if (!Auth::isMember($this->tree)) {
11438add1155SRico Sonntag            return I18N::translate('This information is private and cannot be shown.');
11448add1155SRico Sonntag        }
11458add1155SRico Sonntag
11468add1155SRico Sonntag        $records = $this->topTenOldestAliveQuery('BOTH', $total);
11478add1155SRico Sonntag
1148c0112ce8SGreg Roach        return view('statistics/individuals/top10-nolist', [
11498add1155SRico Sonntag            'records' => $records,
1150c0112ce8SGreg Roach        ]);
11518add1155SRico Sonntag    }
11528add1155SRico Sonntag
11538add1155SRico Sonntag    /**
11548add1155SRico Sonntag     * Find the oldest living individuals.
11558add1155SRico Sonntag     *
11568add1155SRico Sonntag     * @param int $total
11578add1155SRico Sonntag     *
11588add1155SRico Sonntag     * @return string
11598add1155SRico Sonntag     */
11608add1155SRico Sonntag    public function topTenOldestListAlive(int $total = 10): string
11618add1155SRico Sonntag    {
11628add1155SRico Sonntag        if (!Auth::isMember($this->tree)) {
11638add1155SRico Sonntag            return I18N::translate('This information is private and cannot be shown.');
11648add1155SRico Sonntag        }
11658add1155SRico Sonntag
11668add1155SRico Sonntag        $records = $this->topTenOldestAliveQuery('BOTH', $total);
11678add1155SRico Sonntag
1168c0112ce8SGreg Roach        return view('statistics/individuals/top10-list', [
11698add1155SRico Sonntag            'records' => $records,
1170c0112ce8SGreg Roach        ]);
11718add1155SRico Sonntag    }
11728add1155SRico Sonntag
11738add1155SRico Sonntag    /**
11748add1155SRico Sonntag     * Find the oldest living females.
11758add1155SRico Sonntag     *
11768add1155SRico Sonntag     * @param int $total
11778add1155SRico Sonntag     *
11788add1155SRico Sonntag     * @return string
11798add1155SRico Sonntag     */
11808add1155SRico Sonntag    public function topTenOldestFemaleAlive(int $total = 10): string
11818add1155SRico Sonntag    {
11828add1155SRico Sonntag        if (!Auth::isMember($this->tree)) {
11838add1155SRico Sonntag            return I18N::translate('This information is private and cannot be shown.');
11848add1155SRico Sonntag        }
11858add1155SRico Sonntag
11868add1155SRico Sonntag        $records = $this->topTenOldestAliveQuery('F', $total);
11878add1155SRico Sonntag
1188c0112ce8SGreg Roach        return view('statistics/individuals/top10-nolist', [
11898add1155SRico Sonntag            'records' => $records,
1190c0112ce8SGreg Roach        ]);
11918add1155SRico Sonntag    }
11928add1155SRico Sonntag
11938add1155SRico Sonntag    /**
11948add1155SRico Sonntag     * Find the oldest living females.
11958add1155SRico Sonntag     *
11968add1155SRico Sonntag     * @param int $total
11978add1155SRico Sonntag     *
11988add1155SRico Sonntag     * @return string
11998add1155SRico Sonntag     */
12008add1155SRico Sonntag    public function topTenOldestFemaleListAlive(int $total = 10): string
12018add1155SRico Sonntag    {
12028add1155SRico Sonntag        if (!Auth::isMember($this->tree)) {
12038add1155SRico Sonntag            return I18N::translate('This information is private and cannot be shown.');
12048add1155SRico Sonntag        }
12058add1155SRico Sonntag
12068add1155SRico Sonntag        $records = $this->topTenOldestAliveQuery('F', $total);
12078add1155SRico Sonntag
1208c0112ce8SGreg Roach        return view('statistics/individuals/top10-list', [
12098add1155SRico Sonntag            'records' => $records,
1210c0112ce8SGreg Roach        ]);
12118add1155SRico Sonntag    }
12128add1155SRico Sonntag
12138add1155SRico Sonntag    /**
12148add1155SRico Sonntag     * Find the longest lived living males.
12158add1155SRico Sonntag     *
12168add1155SRico Sonntag     * @param int $total
12178add1155SRico Sonntag     *
12188add1155SRico Sonntag     * @return string
12198add1155SRico Sonntag     */
12208add1155SRico Sonntag    public function topTenOldestMaleAlive(int $total = 10): string
12218add1155SRico Sonntag    {
12228add1155SRico Sonntag        if (!Auth::isMember($this->tree)) {
12238add1155SRico Sonntag            return I18N::translate('This information is private and cannot be shown.');
12248add1155SRico Sonntag        }
12258add1155SRico Sonntag
12268add1155SRico Sonntag        $records = $this->topTenOldestAliveQuery('M', $total);
12278add1155SRico Sonntag
1228c0112ce8SGreg Roach        return view('statistics/individuals/top10-nolist', [
12298add1155SRico Sonntag            'records' => $records,
1230c0112ce8SGreg Roach        ]);
12318add1155SRico Sonntag    }
12328add1155SRico Sonntag
12338add1155SRico Sonntag    /**
12348add1155SRico Sonntag     * Find the longest lived living males.
12358add1155SRico Sonntag     *
12368add1155SRico Sonntag     * @param int $total
12378add1155SRico Sonntag     *
12388add1155SRico Sonntag     * @return string
12398add1155SRico Sonntag     */
12408add1155SRico Sonntag    public function topTenOldestMaleListAlive(int $total = 10): string
12418add1155SRico Sonntag    {
12428add1155SRico Sonntag        if (!Auth::isMember($this->tree)) {
12438add1155SRico Sonntag            return I18N::translate('This information is private and cannot be shown.');
12448add1155SRico Sonntag        }
12458add1155SRico Sonntag
12468add1155SRico Sonntag        $records = $this->topTenOldestAliveQuery('M', $total);
12478add1155SRico Sonntag
1248c0112ce8SGreg Roach        return view('statistics/individuals/top10-list', [
12498add1155SRico Sonntag            'records' => $records,
1250c0112ce8SGreg Roach        ]);
12518add1155SRico Sonntag    }
12528add1155SRico Sonntag
12538add1155SRico Sonntag    /**
12548add1155SRico Sonntag     * Find the average lifespan.
12558add1155SRico Sonntag     *
1256c0112ce8SGreg Roach     * @param string $sex        "M", "F" or "BOTH"
12578add1155SRico Sonntag     * @param bool   $show_years
12588add1155SRico Sonntag     *
12598add1155SRico Sonntag     * @return string
12608add1155SRico Sonntag     */
1261c0112ce8SGreg Roach    private function averageLifespanQuery(string $sex, bool $show_years): string
12628add1155SRico Sonntag    {
126344cdc21eSGreg Roach        $prefix = DB::connection()->getTablePrefix();
12648add1155SRico Sonntag
126544cdc21eSGreg Roach        $days = (int) $this->birthAndDeathQuery($sex)
1266e4235cb4SRico Sonntag            ->select(DB::raw('AVG(' . $prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1) AS days'))
126744cdc21eSGreg Roach            ->value('days');
12688add1155SRico Sonntag
12698add1155SRico Sonntag        if ($show_years) {
127044cdc21eSGreg Roach            return $this->calculateAge($days);
12718add1155SRico Sonntag        }
12728add1155SRico Sonntag
127344cdc21eSGreg Roach        return I18N::number((int) ($days / 365.25));
12748add1155SRico Sonntag    }
12758add1155SRico Sonntag
12768add1155SRico Sonntag    /**
12778add1155SRico Sonntag     * Find the average lifespan.
12788add1155SRico Sonntag     *
12798add1155SRico Sonntag     * @param bool $show_years
12808add1155SRico Sonntag     *
12818add1155SRico Sonntag     * @return string
12828add1155SRico Sonntag     */
12838add1155SRico Sonntag    public function averageLifespan($show_years = false): string
12848add1155SRico Sonntag    {
12858add1155SRico Sonntag        return $this->averageLifespanQuery('BOTH', $show_years);
12868add1155SRico Sonntag    }
12878add1155SRico Sonntag
12888add1155SRico Sonntag    /**
12898add1155SRico Sonntag     * Find the average lifespan of females.
12908add1155SRico Sonntag     *
12918add1155SRico Sonntag     * @param bool $show_years
12928add1155SRico Sonntag     *
12938add1155SRico Sonntag     * @return string
12948add1155SRico Sonntag     */
12958add1155SRico Sonntag    public function averageLifespanFemale($show_years = false): string
12968add1155SRico Sonntag    {
12978add1155SRico Sonntag        return $this->averageLifespanQuery('F', $show_years);
12988add1155SRico Sonntag    }
12998add1155SRico Sonntag
13008add1155SRico Sonntag    /**
13018add1155SRico Sonntag     * Find the average male lifespan.
13028add1155SRico Sonntag     *
13038add1155SRico Sonntag     * @param bool $show_years
13048add1155SRico Sonntag     *
13058add1155SRico Sonntag     * @return string
13068add1155SRico Sonntag     */
13078add1155SRico Sonntag    public function averageLifespanMale($show_years = false): string
13088add1155SRico Sonntag    {
13098add1155SRico Sonntag        return $this->averageLifespanQuery('M', $show_years);
13108add1155SRico Sonntag    }
13118add1155SRico Sonntag
13128add1155SRico Sonntag    /**
13138add1155SRico Sonntag     * Convert totals into percentages.
13148add1155SRico Sonntag     *
13158add1155SRico Sonntag     * @param int $count
13168add1155SRico Sonntag     * @param int $total
13178add1155SRico Sonntag     *
13188add1155SRico Sonntag     * @return string
13198add1155SRico Sonntag     */
13208add1155SRico Sonntag    private function getPercentage(int $count, int $total): string
13218add1155SRico Sonntag    {
1322f24db0ceSRico Sonntag        return ($total !== 0) ? I18N::percentage($count / $total, 1) : '';
13238add1155SRico Sonntag    }
13248add1155SRico Sonntag
13258add1155SRico Sonntag    /**
13268add1155SRico Sonntag     * Returns how many individuals exist in the tree.
13278add1155SRico Sonntag     *
13288add1155SRico Sonntag     * @return int
13298add1155SRico Sonntag     */
13308add1155SRico Sonntag    private function totalIndividualsQuery(): int
13318add1155SRico Sonntag    {
13328add1155SRico Sonntag        return DB::table('individuals')
13338add1155SRico Sonntag            ->where('i_file', '=', $this->tree->id())
13348add1155SRico Sonntag            ->count();
13358add1155SRico Sonntag    }
13368add1155SRico Sonntag
13378add1155SRico Sonntag    /**
13388add1155SRico Sonntag     * Count the number of living individuals.
13398add1155SRico Sonntag     *
13408add1155SRico Sonntag     * The totalLiving/totalDeceased queries assume that every dead person will
13418add1155SRico Sonntag     * have a DEAT record. It will not include individuals who were born more
13428add1155SRico Sonntag     * than MAX_ALIVE_AGE years ago, and who have no DEAT record.
13438add1155SRico Sonntag     * A good reason to run the “Add missing DEAT records” batch-update!
13448add1155SRico Sonntag     *
13458add1155SRico Sonntag     * @return int
13468add1155SRico Sonntag     */
13478add1155SRico Sonntag    private function totalLivingQuery(): int
13488add1155SRico Sonntag    {
13493dc8167dSGreg Roach        $query = DB::table('individuals')
13503dc8167dSGreg Roach            ->where('i_file', '=', $this->tree->id());
13513dc8167dSGreg Roach
13523dc8167dSGreg Roach        foreach (Gedcom::DEATH_EVENTS as $death_event) {
135368d9d7c9SRico Sonntag            $query->where('i_gedcom', 'NOT LIKE', "%\n1 " . $death_event . '%');
13543dc8167dSGreg Roach        }
13553dc8167dSGreg Roach
13563dc8167dSGreg Roach        return $query->count();
13578add1155SRico Sonntag    }
13588add1155SRico Sonntag
13598add1155SRico Sonntag    /**
13608add1155SRico Sonntag     * Count the number of dead individuals.
13618add1155SRico Sonntag     *
13628add1155SRico Sonntag     * @return int
13638add1155SRico Sonntag     */
13648add1155SRico Sonntag    private function totalDeceasedQuery(): int
13658add1155SRico Sonntag    {
13668add1155SRico Sonntag        return DB::table('individuals')
13678add1155SRico Sonntag            ->where('i_file', '=', $this->tree->id())
13683dc8167dSGreg Roach            ->where(function (Builder $query): void {
13693dc8167dSGreg Roach                foreach (Gedcom::DEATH_EVENTS as $death_event) {
137068d9d7c9SRico Sonntag                    $query->orWhere('i_gedcom', 'LIKE', "%\n1 " . $death_event . '%');
13713dc8167dSGreg Roach                }
13723dc8167dSGreg Roach            })
13738add1155SRico Sonntag            ->count();
13748add1155SRico Sonntag    }
13758add1155SRico Sonntag
13768add1155SRico Sonntag    /**
13778add1155SRico Sonntag     * Returns the total count of a specific sex.
13788add1155SRico Sonntag     *
13798add1155SRico Sonntag     * @param string $sex The sex to query
13808add1155SRico Sonntag     *
13818add1155SRico Sonntag     * @return int
13828add1155SRico Sonntag     */
13838add1155SRico Sonntag    private function getTotalSexQuery(string $sex): int
13848add1155SRico Sonntag    {
13858add1155SRico Sonntag        return DB::table('individuals')
13868add1155SRico Sonntag            ->where('i_file', '=', $this->tree->id())
13878add1155SRico Sonntag            ->where('i_sex', '=', $sex)
13888add1155SRico Sonntag            ->count();
13898add1155SRico Sonntag    }
13908add1155SRico Sonntag
13918add1155SRico Sonntag    /**
13928add1155SRico Sonntag     * Returns the total number of males.
13938add1155SRico Sonntag     *
13948add1155SRico Sonntag     * @return int
13958add1155SRico Sonntag     */
13968add1155SRico Sonntag    private function totalSexMalesQuery(): int
13978add1155SRico Sonntag    {
13988add1155SRico Sonntag        return $this->getTotalSexQuery('M');
13998add1155SRico Sonntag    }
14008add1155SRico Sonntag
14018add1155SRico Sonntag    /**
14028add1155SRico Sonntag     * Returns the total number of females.
14038add1155SRico Sonntag     *
14048add1155SRico Sonntag     * @return int
14058add1155SRico Sonntag     */
14068add1155SRico Sonntag    private function totalSexFemalesQuery(): int
14078add1155SRico Sonntag    {
14088add1155SRico Sonntag        return $this->getTotalSexQuery('F');
14098add1155SRico Sonntag    }
14108add1155SRico Sonntag
14118add1155SRico Sonntag    /**
14128add1155SRico Sonntag     * Returns the total number of individuals with unknown sex.
14138add1155SRico Sonntag     *
14148add1155SRico Sonntag     * @return int
14158add1155SRico Sonntag     */
14168add1155SRico Sonntag    private function totalSexUnknownQuery(): int
14178add1155SRico Sonntag    {
14188add1155SRico Sonntag        return $this->getTotalSexQuery('U');
14198add1155SRico Sonntag    }
14208add1155SRico Sonntag
14218add1155SRico Sonntag    /**
14228add1155SRico Sonntag     * Count the total families.
14238add1155SRico Sonntag     *
14248add1155SRico Sonntag     * @return int
14258add1155SRico Sonntag     */
14268add1155SRico Sonntag    private function totalFamiliesQuery(): int
14278add1155SRico Sonntag    {
14288add1155SRico Sonntag        return DB::table('families')
14298add1155SRico Sonntag            ->where('f_file', '=', $this->tree->id())
14308add1155SRico Sonntag            ->count();
14318add1155SRico Sonntag    }
14328add1155SRico Sonntag
14338add1155SRico Sonntag    /**
14348add1155SRico Sonntag     * How many individuals have one or more sources.
14358add1155SRico Sonntag     *
14368add1155SRico Sonntag     * @return int
14378add1155SRico Sonntag     */
14388add1155SRico Sonntag    private function totalIndisWithSourcesQuery(): int
14398add1155SRico Sonntag    {
14408add1155SRico Sonntag        return DB::table('individuals')
14418add1155SRico Sonntag            ->select(['i_id'])
14428add1155SRico Sonntag            ->distinct()
14438add1155SRico Sonntag            ->join('link', function (JoinClause $join) {
14448add1155SRico Sonntag                $join->on('i_id', '=', 'l_from')
14458add1155SRico Sonntag                    ->on('i_file', '=', 'l_file');
14468add1155SRico Sonntag            })
14478add1155SRico Sonntag            ->where('l_file', '=', $this->tree->id())
14488add1155SRico Sonntag            ->where('l_type', '=', 'SOUR')
14498add1155SRico Sonntag            ->count('i_id');
14508add1155SRico Sonntag    }
14518add1155SRico Sonntag
14528add1155SRico Sonntag    /**
14538add1155SRico Sonntag     * Count the families with source records.
14548add1155SRico Sonntag     *
14558add1155SRico Sonntag     * @return int
14568add1155SRico Sonntag     */
14578add1155SRico Sonntag    private function totalFamsWithSourcesQuery(): int
14588add1155SRico Sonntag    {
14598add1155SRico Sonntag        return DB::table('families')
14608add1155SRico Sonntag            ->select(['f_id'])
14618add1155SRico Sonntag            ->distinct()
14628add1155SRico Sonntag            ->join('link', function (JoinClause $join) {
14638add1155SRico Sonntag                $join->on('f_id', '=', 'l_from')
14648add1155SRico Sonntag                    ->on('f_file', '=', 'l_file');
14658add1155SRico Sonntag            })
14668add1155SRico Sonntag            ->where('l_file', '=', $this->tree->id())
14678add1155SRico Sonntag            ->where('l_type', '=', 'SOUR')
14688add1155SRico Sonntag            ->count('f_id');
14698add1155SRico Sonntag    }
14708add1155SRico Sonntag
14718add1155SRico Sonntag    /**
14728add1155SRico Sonntag     * Count the number of repositories.
14738add1155SRico Sonntag     *
14748add1155SRico Sonntag     * @return int
14758add1155SRico Sonntag     */
14768add1155SRico Sonntag    private function totalRepositoriesQuery(): int
14778add1155SRico Sonntag    {
14788add1155SRico Sonntag        return DB::table('other')
14798add1155SRico Sonntag            ->where('o_file', '=', $this->tree->id())
14808add1155SRico Sonntag            ->where('o_type', '=', 'REPO')
14818add1155SRico Sonntag            ->count();
14828add1155SRico Sonntag    }
14838add1155SRico Sonntag
14848add1155SRico Sonntag    /**
14858add1155SRico Sonntag     * Count the total number of sources.
14868add1155SRico Sonntag     *
14878add1155SRico Sonntag     * @return int
14888add1155SRico Sonntag     */
14898add1155SRico Sonntag    private function totalSourcesQuery(): int
14908add1155SRico Sonntag    {
14918add1155SRico Sonntag        return DB::table('sources')
14928add1155SRico Sonntag            ->where('s_file', '=', $this->tree->id())
14938add1155SRico Sonntag            ->count();
14948add1155SRico Sonntag    }
14958add1155SRico Sonntag
14968add1155SRico Sonntag    /**
14978add1155SRico Sonntag     * Count the number of notes.
14988add1155SRico Sonntag     *
14998add1155SRico Sonntag     * @return int
15008add1155SRico Sonntag     */
15018add1155SRico Sonntag    private function totalNotesQuery(): int
15028add1155SRico Sonntag    {
15038add1155SRico Sonntag        return DB::table('other')
15048add1155SRico Sonntag            ->where('o_file', '=', $this->tree->id())
15058add1155SRico Sonntag            ->where('o_type', '=', 'NOTE')
15068add1155SRico Sonntag            ->count();
15078add1155SRico Sonntag    }
15088add1155SRico Sonntag
15098add1155SRico Sonntag    /**
15108add1155SRico Sonntag     * Returns the total number of records.
15118add1155SRico Sonntag     *
15128add1155SRico Sonntag     * @return int
15138add1155SRico Sonntag     */
15148add1155SRico Sonntag    private function totalRecordsQuery(): int
15158add1155SRico Sonntag    {
15168add1155SRico Sonntag        return $this->totalIndividualsQuery()
15178add1155SRico Sonntag            + $this->totalFamiliesQuery()
15188add1155SRico Sonntag            + $this->totalNotesQuery()
15198add1155SRico Sonntag            + $this->totalRepositoriesQuery()
15208add1155SRico Sonntag            + $this->totalSourcesQuery();
15218add1155SRico Sonntag    }
15228add1155SRico Sonntag
15238add1155SRico Sonntag    /**
15248add1155SRico Sonntag     * @inheritDoc
15258add1155SRico Sonntag     */
15268add1155SRico Sonntag    public function totalRecords(): string
15278add1155SRico Sonntag    {
15288add1155SRico Sonntag        return I18N::number($this->totalRecordsQuery());
15298add1155SRico Sonntag    }
15308add1155SRico Sonntag
15318add1155SRico Sonntag    /**
15328add1155SRico Sonntag     * @inheritDoc
15338add1155SRico Sonntag     */
15348add1155SRico Sonntag    public function totalIndividuals(): string
15358add1155SRico Sonntag    {
15368add1155SRico Sonntag        return I18N::number($this->totalIndividualsQuery());
15378add1155SRico Sonntag    }
15388add1155SRico Sonntag
15398add1155SRico Sonntag    /**
15408add1155SRico Sonntag     * Count the number of living individuals.
15418add1155SRico Sonntag     *
15428add1155SRico Sonntag     * @return string
15438add1155SRico Sonntag     */
15448add1155SRico Sonntag    public function totalLiving(): string
15458add1155SRico Sonntag    {
15468add1155SRico Sonntag        return I18N::number($this->totalLivingQuery());
15478add1155SRico Sonntag    }
15488add1155SRico Sonntag
15498add1155SRico Sonntag    /**
15508add1155SRico Sonntag     * Count the number of dead individuals.
15518add1155SRico Sonntag     *
15528add1155SRico Sonntag     * @return string
15538add1155SRico Sonntag     */
15548add1155SRico Sonntag    public function totalDeceased(): string
15558add1155SRico Sonntag    {
15568add1155SRico Sonntag        return I18N::number($this->totalDeceasedQuery());
15578add1155SRico Sonntag    }
15588add1155SRico Sonntag
15598add1155SRico Sonntag    /**
15608add1155SRico Sonntag     * @inheritDoc
15618add1155SRico Sonntag     */
15628add1155SRico Sonntag    public function totalSexMales(): string
15638add1155SRico Sonntag    {
15648add1155SRico Sonntag        return I18N::number($this->totalSexMalesQuery());
15658add1155SRico Sonntag    }
15668add1155SRico Sonntag
15678add1155SRico Sonntag    /**
15688add1155SRico Sonntag     * @inheritDoc
15698add1155SRico Sonntag     */
15708add1155SRico Sonntag    public function totalSexFemales(): string
15718add1155SRico Sonntag    {
15728add1155SRico Sonntag        return I18N::number($this->totalSexFemalesQuery());
15738add1155SRico Sonntag    }
15748add1155SRico Sonntag
15758add1155SRico Sonntag    /**
15768add1155SRico Sonntag     * @inheritDoc
15778add1155SRico Sonntag     */
15788add1155SRico Sonntag    public function totalSexUnknown(): string
15798add1155SRico Sonntag    {
15808add1155SRico Sonntag        return I18N::number($this->totalSexUnknownQuery());
15818add1155SRico Sonntag    }
15828add1155SRico Sonntag
15838add1155SRico Sonntag    /**
15848add1155SRico Sonntag     * @inheritDoc
15858add1155SRico Sonntag     */
15868add1155SRico Sonntag    public function totalFamilies(): string
15878add1155SRico Sonntag    {
15888add1155SRico Sonntag        return I18N::number($this->totalFamiliesQuery());
15898add1155SRico Sonntag    }
15908add1155SRico Sonntag
15918add1155SRico Sonntag    /**
15928add1155SRico Sonntag     * How many individuals have one or more sources.
15938add1155SRico Sonntag     *
15948add1155SRico Sonntag     * @return string
15958add1155SRico Sonntag     */
15968add1155SRico Sonntag    public function totalIndisWithSources(): string
15978add1155SRico Sonntag    {
15988add1155SRico Sonntag        return I18N::number($this->totalIndisWithSourcesQuery());
15998add1155SRico Sonntag    }
16008add1155SRico Sonntag
16018add1155SRico Sonntag    /**
16028add1155SRico Sonntag     * Count the families with with source records.
16038add1155SRico Sonntag     *
16048add1155SRico Sonntag     * @return string
16058add1155SRico Sonntag     */
16068add1155SRico Sonntag    public function totalFamsWithSources(): string
16078add1155SRico Sonntag    {
16088add1155SRico Sonntag        return I18N::number($this->totalFamsWithSourcesQuery());
16098add1155SRico Sonntag    }
16108add1155SRico Sonntag
16118add1155SRico Sonntag    /**
16128add1155SRico Sonntag     * @inheritDoc
16138add1155SRico Sonntag     */
16148add1155SRico Sonntag    public function totalRepositories(): string
16158add1155SRico Sonntag    {
16168add1155SRico Sonntag        return I18N::number($this->totalRepositoriesQuery());
16178add1155SRico Sonntag    }
16188add1155SRico Sonntag
16198add1155SRico Sonntag    /**
16208add1155SRico Sonntag     * @inheritDoc
16218add1155SRico Sonntag     */
16228add1155SRico Sonntag    public function totalSources(): string
16238add1155SRico Sonntag    {
16248add1155SRico Sonntag        return I18N::number($this->totalSourcesQuery());
16258add1155SRico Sonntag    }
16268add1155SRico Sonntag
16278add1155SRico Sonntag    /**
16288add1155SRico Sonntag     * @inheritDoc
16298add1155SRico Sonntag     */
16308add1155SRico Sonntag    public function totalNotes(): string
16318add1155SRico Sonntag    {
16328add1155SRico Sonntag        return I18N::number($this->totalNotesQuery());
16338add1155SRico Sonntag    }
16348add1155SRico Sonntag
16358add1155SRico Sonntag    /**
16368add1155SRico Sonntag     * @inheritDoc
16378add1155SRico Sonntag     */
16388add1155SRico Sonntag    public function totalIndividualsPercentage(): string
16398add1155SRico Sonntag    {
16408add1155SRico Sonntag        return $this->getPercentage(
16418add1155SRico Sonntag            $this->totalIndividualsQuery(),
16428add1155SRico Sonntag            $this->totalRecordsQuery()
16438add1155SRico Sonntag        );
16448add1155SRico Sonntag    }
16458add1155SRico Sonntag
16468add1155SRico Sonntag    /**
16478add1155SRico Sonntag     * @inheritDoc
16488add1155SRico Sonntag     */
16498add1155SRico Sonntag    public function totalFamiliesPercentage(): string
16508add1155SRico Sonntag    {
16518add1155SRico Sonntag        return $this->getPercentage(
16528add1155SRico Sonntag            $this->totalFamiliesQuery(),
16538add1155SRico Sonntag            $this->totalRecordsQuery()
16548add1155SRico Sonntag        );
16558add1155SRico Sonntag    }
16568add1155SRico Sonntag
16578add1155SRico Sonntag    /**
16588add1155SRico Sonntag     * @inheritDoc
16598add1155SRico Sonntag     */
16608add1155SRico Sonntag    public function totalRepositoriesPercentage(): string
16618add1155SRico Sonntag    {
16628add1155SRico Sonntag        return $this->getPercentage(
16638add1155SRico Sonntag            $this->totalRepositoriesQuery(),
16648add1155SRico Sonntag            $this->totalRecordsQuery()
16658add1155SRico Sonntag        );
16668add1155SRico Sonntag    }
16678add1155SRico Sonntag
16688add1155SRico Sonntag    /**
16698add1155SRico Sonntag     * @inheritDoc
16708add1155SRico Sonntag     */
16718add1155SRico Sonntag    public function totalSourcesPercentage(): string
16728add1155SRico Sonntag    {
16738add1155SRico Sonntag        return $this->getPercentage(
16748add1155SRico Sonntag            $this->totalSourcesQuery(),
16758add1155SRico Sonntag            $this->totalRecordsQuery()
16768add1155SRico Sonntag        );
16778add1155SRico Sonntag    }
16788add1155SRico Sonntag
16798add1155SRico Sonntag    /**
16808add1155SRico Sonntag     * @inheritDoc
16818add1155SRico Sonntag     */
16828add1155SRico Sonntag    public function totalNotesPercentage(): string
16838add1155SRico Sonntag    {
16848add1155SRico Sonntag        return $this->getPercentage(
16858add1155SRico Sonntag            $this->totalNotesQuery(),
16868add1155SRico Sonntag            $this->totalRecordsQuery()
16878add1155SRico Sonntag        );
16888add1155SRico Sonntag    }
16898add1155SRico Sonntag
16908add1155SRico Sonntag    /**
16918add1155SRico Sonntag     * @inheritDoc
16928add1155SRico Sonntag     */
16938add1155SRico Sonntag    public function totalLivingPercentage(): string
16948add1155SRico Sonntag    {
16958add1155SRico Sonntag        return $this->getPercentage(
16968add1155SRico Sonntag            $this->totalLivingQuery(),
16978add1155SRico Sonntag            $this->totalIndividualsQuery()
16988add1155SRico Sonntag        );
16998add1155SRico Sonntag    }
17008add1155SRico Sonntag
17018add1155SRico Sonntag    /**
17028add1155SRico Sonntag     * @inheritDoc
17038add1155SRico Sonntag     */
17048add1155SRico Sonntag    public function totalDeceasedPercentage(): string
17058add1155SRico Sonntag    {
17068add1155SRico Sonntag        return $this->getPercentage(
17078add1155SRico Sonntag            $this->totalDeceasedQuery(),
17088add1155SRico Sonntag            $this->totalIndividualsQuery()
17098add1155SRico Sonntag        );
17108add1155SRico Sonntag    }
17118add1155SRico Sonntag
17128add1155SRico Sonntag    /**
17138add1155SRico Sonntag     * @inheritDoc
17148add1155SRico Sonntag     */
17158add1155SRico Sonntag    public function totalSexMalesPercentage(): string
17168add1155SRico Sonntag    {
17178add1155SRico Sonntag        return $this->getPercentage(
17188add1155SRico Sonntag            $this->totalSexMalesQuery(),
17198add1155SRico Sonntag            $this->totalIndividualsQuery()
17208add1155SRico Sonntag        );
17218add1155SRico Sonntag    }
17228add1155SRico Sonntag
17238add1155SRico Sonntag    /**
17248add1155SRico Sonntag     * @inheritDoc
17258add1155SRico Sonntag     */
17268add1155SRico Sonntag    public function totalSexFemalesPercentage(): string
17278add1155SRico Sonntag    {
17288add1155SRico Sonntag        return $this->getPercentage(
17298add1155SRico Sonntag            $this->totalSexFemalesQuery(),
17308add1155SRico Sonntag            $this->totalIndividualsQuery()
17318add1155SRico Sonntag        );
17328add1155SRico Sonntag    }
17338add1155SRico Sonntag
17348add1155SRico Sonntag    /**
17358add1155SRico Sonntag     * @inheritDoc
17368add1155SRico Sonntag     */
17378add1155SRico Sonntag    public function totalSexUnknownPercentage(): string
17388add1155SRico Sonntag    {
17398add1155SRico Sonntag        return $this->getPercentage(
17408add1155SRico Sonntag            $this->totalSexUnknownQuery(),
17418add1155SRico Sonntag            $this->totalIndividualsQuery()
17428add1155SRico Sonntag        );
17438add1155SRico Sonntag    }
17448add1155SRico Sonntag
17458add1155SRico Sonntag    /**
17468add1155SRico Sonntag     * Create a chart of common given names.
17478add1155SRico Sonntag     *
17488add1155SRico Sonntag     * @param string|null $color_from
17498add1155SRico Sonntag     * @param string|null $color_to
17508add1155SRico Sonntag     * @param int         $maxtoshow
17518add1155SRico Sonntag     *
17528add1155SRico Sonntag     * @return string
17538add1155SRico Sonntag     */
17548add1155SRico Sonntag    public function chartCommonGiven(
17558add1155SRico Sonntag        string $color_from = null,
17568add1155SRico Sonntag        string $color_to = null,
17578add1155SRico Sonntag        int $maxtoshow = 7
17588add1155SRico Sonntag    ): string {
17598add1155SRico Sonntag        $tot_indi = $this->totalIndividualsQuery();
17608add1155SRico Sonntag        $given    = $this->commonGivenQuery('B', 'chart', false, 1, $maxtoshow);
17618add1155SRico Sonntag
176288de55fdSRico Sonntag        if (empty($given)) {
1763dd7dd2a1SRico Sonntag            return I18N::translate('This information is not available.');
176488de55fdSRico Sonntag        }
176588de55fdSRico Sonntag
176693ccd686SRico Sonntag        return (new ChartCommonGiven())
176788de55fdSRico Sonntag            ->chartCommonGiven($tot_indi, $given, $color_from, $color_to);
17688add1155SRico Sonntag    }
17698add1155SRico Sonntag
17708add1155SRico Sonntag    /**
17718add1155SRico Sonntag     * Create a chart of common surnames.
17728add1155SRico Sonntag     *
17738add1155SRico Sonntag     * @param string|null $color_from
17748add1155SRico Sonntag     * @param string|null $color_to
17758add1155SRico Sonntag     * @param int         $number_of_surnames
17768add1155SRico Sonntag     *
17778add1155SRico Sonntag     * @return string
17788add1155SRico Sonntag     */
17798add1155SRico Sonntag    public function chartCommonSurnames(
17808add1155SRico Sonntag        string $color_from = null,
17818add1155SRico Sonntag        string $color_to = null,
17828add1155SRico Sonntag        int $number_of_surnames = 10
17838add1155SRico Sonntag    ): string {
17848add1155SRico Sonntag        $tot_indi     = $this->totalIndividualsQuery();
17858add1155SRico Sonntag        $all_surnames = $this->topSurnames($number_of_surnames, 0);
17868add1155SRico Sonntag
178788de55fdSRico Sonntag        if (empty($all_surnames)) {
1788dd7dd2a1SRico Sonntag            return I18N::translate('This information is not available.');
178988de55fdSRico Sonntag        }
179088de55fdSRico Sonntag
17918add1155SRico Sonntag        return (new ChartCommonSurname($this->tree))
179288de55fdSRico Sonntag            ->chartCommonSurnames($tot_indi, $all_surnames, $color_from, $color_to);
17938add1155SRico Sonntag    }
17948add1155SRico Sonntag
17958add1155SRico Sonntag    /**
17968add1155SRico Sonntag     * Create a chart showing mortality.
17978add1155SRico Sonntag     *
17988add1155SRico Sonntag     * @param string|null $color_living
17998add1155SRico Sonntag     * @param string|null $color_dead
18008add1155SRico Sonntag     *
18018add1155SRico Sonntag     * @return string
18028add1155SRico Sonntag     */
180388de55fdSRico Sonntag    public function chartMortality(string $color_living = null, string $color_dead = null): string
18048add1155SRico Sonntag    {
18058add1155SRico Sonntag        $tot_l = $this->totalLivingQuery();
18068add1155SRico Sonntag        $tot_d = $this->totalDeceasedQuery();
18078add1155SRico Sonntag
180893ccd686SRico Sonntag        return (new ChartMortality())
180988de55fdSRico Sonntag            ->chartMortality($tot_l, $tot_d, $color_living, $color_dead);
18108add1155SRico Sonntag    }
18118add1155SRico Sonntag
18128add1155SRico Sonntag    /**
18138add1155SRico Sonntag     * Create a chart showing individuals with/without sources.
18148add1155SRico Sonntag     *
18158add1155SRico Sonntag     * @param string|null $color_from
18168add1155SRico Sonntag     * @param string|null $color_to
18178add1155SRico Sonntag     *
18188add1155SRico Sonntag     * @return string
18198add1155SRico Sonntag     */
18208add1155SRico Sonntag    public function chartIndisWithSources(
18218add1155SRico Sonntag        string $color_from = null,
18228add1155SRico Sonntag        string $color_to   = null
18238add1155SRico Sonntag    ): string {
18248add1155SRico Sonntag        $tot_indi        = $this->totalIndividualsQuery();
18258add1155SRico Sonntag        $tot_indi_source = $this->totalIndisWithSourcesQuery();
18268add1155SRico Sonntag
182793ccd686SRico Sonntag        return (new ChartIndividualWithSources())
182888de55fdSRico Sonntag            ->chartIndisWithSources($tot_indi, $tot_indi_source, $color_from, $color_to);
18298add1155SRico Sonntag    }
18308add1155SRico Sonntag
18318add1155SRico Sonntag    /**
18328add1155SRico Sonntag     * Create a chart of individuals with/without sources.
18338add1155SRico Sonntag     *
18348add1155SRico Sonntag     * @param string|null $color_from
18358add1155SRico Sonntag     * @param string|null $color_to
18368add1155SRico Sonntag     *
18378add1155SRico Sonntag     * @return string
18388add1155SRico Sonntag     */
18398add1155SRico Sonntag    public function chartFamsWithSources(
18408add1155SRico Sonntag        string $color_from = null,
18418add1155SRico Sonntag        string $color_to   = null
18428add1155SRico Sonntag    ): string {
18438add1155SRico Sonntag        $tot_fam        = $this->totalFamiliesQuery();
18448add1155SRico Sonntag        $tot_fam_source = $this->totalFamsWithSourcesQuery();
18458add1155SRico Sonntag
184693ccd686SRico Sonntag        return (new ChartFamilyWithSources())
184788de55fdSRico Sonntag            ->chartFamsWithSources($tot_fam, $tot_fam_source, $color_from, $color_to);
18488add1155SRico Sonntag    }
18498add1155SRico Sonntag
18508add1155SRico Sonntag    /**
18518add1155SRico Sonntag     * @inheritDoc
18528add1155SRico Sonntag     */
18538add1155SRico Sonntag    public function chartSex(
18548add1155SRico Sonntag        string $color_female  = null,
18558add1155SRico Sonntag        string $color_male    = null,
18568add1155SRico Sonntag        string $color_unknown = null
18578add1155SRico Sonntag    ): string {
18588add1155SRico Sonntag        $tot_m = $this->totalSexMalesQuery();
18598add1155SRico Sonntag        $tot_f = $this->totalSexFemalesQuery();
18608add1155SRico Sonntag        $tot_u = $this->totalSexUnknownQuery();
18618add1155SRico Sonntag
186293ccd686SRico Sonntag        return (new ChartSex())
186388de55fdSRico Sonntag            ->chartSex($tot_m, $tot_f, $tot_u, $color_female, $color_male, $color_unknown);
18648add1155SRico Sonntag    }
186544cdc21eSGreg Roach
186644cdc21eSGreg Roach    /**
186744cdc21eSGreg Roach     * Query individuals, with their births and deaths.
186844cdc21eSGreg Roach     *
186944cdc21eSGreg Roach     * @param string $sex
187044cdc21eSGreg Roach     *
187144cdc21eSGreg Roach     * @return Builder
187244cdc21eSGreg Roach     */
1873e2cbf57aSGreg Roach    private function birthAndDeathQuery(string $sex): Builder
1874e2cbf57aSGreg Roach    {
187544cdc21eSGreg Roach        $query = DB::table('individuals')
187644cdc21eSGreg Roach            ->where('i_file', '=', $this->tree->id())
187744cdc21eSGreg Roach            ->join('dates AS birth', function (JoinClause $join): void {
187844cdc21eSGreg Roach                $join
187944cdc21eSGreg Roach                    ->on('birth.d_file', '=', 'i_file')
188044cdc21eSGreg Roach                    ->on('birth.d_gid', '=', 'i_id');
188144cdc21eSGreg Roach            })
188244cdc21eSGreg Roach            ->join('dates AS death', function (JoinClause $join): void {
188344cdc21eSGreg Roach                $join
188444cdc21eSGreg Roach                    ->on('death.d_file', '=', 'i_file')
188544cdc21eSGreg Roach                    ->on('death.d_gid', '=', 'i_id');
188644cdc21eSGreg Roach            })
188744cdc21eSGreg Roach            ->where('birth.d_fact', '=', 'BIRT')
188844cdc21eSGreg Roach            ->where('death.d_fact', '=', 'DEAT')
188944cdc21eSGreg Roach            ->whereColumn('death.d_julianday1', '>=', 'birth.d_julianday2')
189044cdc21eSGreg Roach            ->where('birth.d_julianday2', '<>', 0);
189144cdc21eSGreg Roach
189244cdc21eSGreg Roach        if ($sex === 'M' || $sex === 'F') {
189344cdc21eSGreg Roach            $query->where('i_sex', '=', $sex);
189444cdc21eSGreg Roach        }
189544cdc21eSGreg Roach
189644cdc21eSGreg Roach        return $query;
189744cdc21eSGreg Roach    }
18988add1155SRico Sonntag}
1899