1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* webtrees-lib: MyArtJaub library for webtrees |
4
|
|
|
* |
5
|
|
|
* @package MyArtJaub\Webtrees |
6
|
|
|
* @subpackage Sosa |
7
|
|
|
* @author Jonathan Jaubart <[email protected]> |
8
|
|
|
* @copyright Copyright (c) 2009-2016, Jonathan Jaubart |
9
|
|
|
* @license http://www.gnu.org/licenses/gpl.html GNU General Public License, version 3 |
10
|
|
|
*/ |
11
|
|
|
namespace MyArtJaub\Webtrees\Module\Sosa\Model; |
12
|
|
|
|
13
|
|
|
use Fisharebest\Webtrees\Tree; |
14
|
|
|
use Fisharebest\Webtrees\User; |
15
|
|
|
use Fisharebest\Webtrees\Database; |
16
|
|
|
use MyArtJaub\Webtrees\Functions\Functions; |
17
|
|
|
use Fisharebest\Webtrees\Auth; |
18
|
|
|
use Fisharebest\Webtrees\Individual; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Provide Sosa data access |
22
|
|
|
*/ |
23
|
|
|
class SosaProvider { |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Maximum number of generation the database is able to hold. |
27
|
|
|
* @var int MAX_DB_GENERATIONS |
28
|
|
|
*/ |
29
|
|
|
const MAX_DB_GENERATIONS = 64; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* System's default user (ID -1 in the database |
33
|
|
|
* @var User $default_user |
34
|
|
|
*/ |
35
|
|
|
protected static $default_user; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Reference user |
39
|
|
|
* @var User $user |
40
|
|
|
*/ |
41
|
|
|
protected $user; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Reference tree |
45
|
|
|
* @var Tree $tree |
46
|
|
|
*/ |
47
|
|
|
protected $tree; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Cached list of Sosa Individuals by generation |
51
|
|
|
* Format: key = generation, value = array ( sosa => Individual ID) |
52
|
|
|
* @var array $sosa_list_by_gen |
53
|
|
|
*/ |
54
|
|
|
protected $sosa_list_by_gen; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* Cached list of Sosa Families by generation |
58
|
|
|
* Format: key = generation, value = array ( sosa => Family ID) |
59
|
|
|
* @var unknown $sosa_fam_list_by_gen |
60
|
|
|
*/ |
61
|
|
|
protected $sosa_fam_list_by_gen; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Cached array of statistics by generation |
65
|
|
|
* Format: key = generation, |
66
|
|
|
* value = array( |
67
|
|
|
* sosaCount, sosaTotalCount, diffSosaTotalCount, firstBirth, lastBirth, avgBirth |
68
|
|
|
* ) |
69
|
|
|
* @var array $statistics_tab |
70
|
|
|
*/ |
71
|
|
|
protected $statistics_tab; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Has the provider's initialisation completed |
75
|
|
|
* @var bool $is_setup |
76
|
|
|
*/ |
77
|
|
|
protected $is_setup; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Constructor for Sosa Provider. |
81
|
|
|
* A provider is defined in relation to a specific tree and reference user. |
82
|
|
|
* |
83
|
|
|
* @param Tree $tree |
84
|
|
|
* @param User $user |
85
|
|
|
*/ |
86
|
|
|
public function __construct(Tree $tree, User $user = null) { |
87
|
|
|
if(self::$default_user === null) |
88
|
|
|
self::$default_user = User::find(-1); |
89
|
|
|
|
90
|
|
|
$this->tree = $tree; |
91
|
|
|
$this->user = $user; |
92
|
|
|
$this->is_setup = true; |
93
|
|
|
if($this->user === null) $this->user = Auth::user(); |
94
|
|
|
if(strlen($this->user->getUserId()) == 0) $this->user = self::$default_user; |
95
|
|
|
|
96
|
|
|
// Check if the user, or the default user, has a root already setup; |
97
|
|
|
if(empty($this->getRootIndiId())) { |
98
|
|
|
if($this->user == self::$default_user) { // If the default user is not setup |
99
|
|
|
$this->is_setup = false; |
100
|
|
|
} |
101
|
|
|
else { |
102
|
|
|
$this->user = self::$default_user; |
103
|
|
|
$this->is_setup = $this->getRootIndiId() === null; |
104
|
|
|
} |
105
|
|
|
} |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Returns is the Provider has been successfully set up |
110
|
|
|
* @return bool |
111
|
|
|
*/ |
112
|
|
|
public function isSetup() { |
113
|
|
|
return $this->is_setup; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* Return the root individual ID for the reference tree and user. |
118
|
|
|
* @return string Individual ID |
119
|
|
|
*/ |
120
|
|
|
public function getRootIndiId() { |
121
|
|
|
return $this->tree->getUserPreference($this->user, 'MAJ_SOSA_ROOT_ID'); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Return the root individual for the reference tree and user. |
126
|
|
|
* @return Individual Individual |
127
|
|
|
*/ |
128
|
|
|
public function getRootIndi() { |
129
|
|
|
$root_indi_id = $this->getRootIndiId(); |
130
|
|
|
if(!empty($root_indi_id)) { |
131
|
|
|
return Individual::getInstance($root_indi_id, $this->tree); |
132
|
|
|
} |
133
|
|
|
return null; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/***************** |
137
|
|
|
* DATA CRUD LAYER |
138
|
|
|
*****************/ |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Remove all Sosa entries related to the gedcom file and user |
142
|
|
|
*/ |
143
|
|
|
public function deleteAll() { |
144
|
|
|
if(!$this->is_setup) return; |
145
|
|
|
Database::prepare( |
146
|
|
|
'DELETE FROM `##maj_sosa`'. |
147
|
|
|
' WHERE majs_gedcom_id= :tree_id and majs_user_id = :user_id ') |
148
|
|
|
->execute(array( |
149
|
|
|
'tree_id' => $this->tree->getTreeId(), |
150
|
|
|
'user_id' => $this->user->getUserId() |
151
|
|
|
)); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Remove all ancestors of a sosa number |
156
|
|
|
* |
157
|
|
|
* @param int $sosa |
158
|
|
|
*/ |
159
|
|
|
public function deleteAncestors($sosa) { |
160
|
|
|
if(!$this->is_setup) return; |
161
|
|
|
$gen = Functions::getGeneration($sosa); |
162
|
|
|
Database::prepare( |
163
|
|
|
'DELETE FROM `##maj_sosa`'. |
164
|
|
|
' WHERE majs_gedcom_id=:tree_id and majs_user_id = :user_id' . |
165
|
|
|
' AND majs_gen >= :gen' . |
166
|
|
|
' AND FLOOR(majs_sosa / (POW(2, (majs_gen - :gen)))) = :sosa' |
167
|
|
|
)->execute(array( |
168
|
|
|
'tree_id' => $this->tree->getTreeId(), |
169
|
|
|
'user_id' => $this->user->getUserId(), |
170
|
|
|
'gen' => $gen, |
171
|
|
|
'sosa' => $sosa |
172
|
|
|
)); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Insert (or update if already existing) a list of Sosa individuals |
177
|
|
|
* @param array $sosa_records |
178
|
|
|
*/ |
179
|
|
|
public function insertOrUpdate($sosa_records) { |
180
|
|
|
if(!$this->is_setup) return; |
181
|
|
|
|
182
|
|
|
$treeid = $this->tree->getTreeId(); |
183
|
|
|
$userid = $this->user->getUserId(); |
184
|
|
|
$questionmarks_table = array(); |
185
|
|
|
$values_table = array(); |
186
|
|
|
|
187
|
|
|
$i = 0; |
188
|
|
|
foreach ($sosa_records as $row) { |
189
|
|
|
$gen = Functions::getGeneration($row['sosa']); |
190
|
|
|
if($gen <= self::MAX_DB_GENERATIONS) { |
191
|
|
|
$questionmarks_table[] = |
192
|
|
|
'(:tree_id'.$i.', :user_id'.$i.', :sosa'.$i.', :indi_id'.$i.', :gen'.$i.', :byear'.$i.', :dyear'.$i.')'; |
193
|
|
|
$values_table = array_merge( |
194
|
|
|
$values_table, |
195
|
|
|
array( |
196
|
|
|
'tree_id'.$i => $treeid, |
197
|
|
|
'user_id'.$i => $userid, |
198
|
|
|
'sosa'.$i => $row['sosa'], |
199
|
|
|
'indi_id'.$i => $row['indi'], |
200
|
|
|
'gen'.$i => Functions::getGeneration($row['sosa']), |
201
|
|
|
'byear'.$i => $row['birth_year'], |
202
|
|
|
'dyear'.$i => $row['death_year'] |
203
|
|
|
) |
204
|
|
|
); |
205
|
|
|
} |
206
|
|
|
$i++; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
$sql = 'REPLACE INTO `##maj_sosa`' . |
210
|
|
|
' (majs_gedcom_id, majs_user_id, majs_sosa, majs_i_id, majs_gen, majs_birth_year, majs_death_year)' . |
211
|
|
|
' VALUES '. implode(',', $questionmarks_table); |
212
|
|
|
Database::prepare($sql)->execute($values_table); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/**************** |
216
|
|
|
* SIMPLE QUERIES |
217
|
|
|
****************/ |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* Returns the list of Sosa numbers to which an individual is related. |
221
|
|
|
* Format: key = sosa number, value = generation for the Sosa number |
222
|
|
|
* |
223
|
|
|
* @param Individual $indi |
224
|
|
|
* @return array Array of sosa numbers |
225
|
|
|
*/ |
226
|
|
View Code Duplication |
public function getSosaNumbers(Individual $indi) { |
|
|
|
|
227
|
|
|
if(!$this->is_setup) return array(); |
228
|
|
|
return Database::prepare( |
229
|
|
|
'SELECT majs_sosa, majs_gen FROM `##maj_sosa`'. |
230
|
|
|
' WHERE majs_i_id=:indi_id AND majs_gedcom_id=:tree_id AND majs_user_id=:user_id' |
231
|
|
|
)->execute(array( |
232
|
|
|
'indi_id' => $indi->getXref(), |
233
|
|
|
'tree_id' => $this->tree->getTreeId(), |
234
|
|
|
'user_id' => $this->user->getUserId() |
235
|
|
|
))->fetchAssoc(); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* Get the last generation of Sosa ancestors |
240
|
|
|
* |
241
|
|
|
* @return number Last generation if found, 1 otherwise |
242
|
|
|
*/ |
243
|
|
View Code Duplication |
public function getLastGeneration() { |
|
|
|
|
244
|
|
|
if(!$this->is_setup) return; |
245
|
|
|
return Database::prepare( |
246
|
|
|
'SELECT MAX(majs_gen) FROM `##maj_sosa`'. |
247
|
|
|
' WHERE majs_gedcom_id=:tree_id AND majs_user_id=:user_id' |
248
|
|
|
)->execute(array( |
249
|
|
|
'tree_id' => $this->tree->getTreeId(), |
250
|
|
|
'user_id' => $this->user->getUserId() |
251
|
|
|
))->fetchOne() ?: 1; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/************* |
255
|
|
|
* SOSA LISTS |
256
|
|
|
*************/ |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Return the list of all sosas, with the generations it belongs to |
260
|
|
|
* |
261
|
|
|
* @param int $ged_id ID of the gedcom file |
|
|
|
|
262
|
|
|
* @return array Associative array of Sosa ancestors, with their generation, comma separated |
263
|
|
|
*/ |
264
|
|
View Code Duplication |
public function getAllSosaWithGenerations(){ |
|
|
|
|
265
|
|
|
if(!$this->is_setup) return array(); |
266
|
|
|
return Database::prepare( |
267
|
|
|
'SELECT majs_i_id AS indi,' . |
268
|
|
|
' GROUP_CONCAT(DISTINCT majs_gen ORDER BY majs_gen ASC SEPARATOR ",") AS generations' . |
269
|
|
|
' FROM `##maj_sosa`' . |
270
|
|
|
' WHERE majs_gedcom_id=:tree_id AND majs_user_id=:user_id' . |
271
|
|
|
' GROUP BY majs_i_id' |
272
|
|
|
)->execute(array( |
273
|
|
|
'tree_id' => $this->tree->getTreeId(), |
274
|
|
|
'user_id' => $this->user->getUserId() |
275
|
|
|
))->fetchAssoc(); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* Get an associative array of Sosa individuals in generation G. Keys are Sosa numbers, values individuals. |
280
|
|
|
* |
281
|
|
|
* @param number $gen Generation |
282
|
|
|
* @return array Array of Sosa individuals |
283
|
|
|
*/ |
284
|
|
|
public function getSosaListAtGeneration($gen){ |
285
|
|
|
if(!$this->is_setup) return array(); |
286
|
|
|
if(!$this->sosa_list_by_gen) |
|
|
|
|
287
|
|
|
$this->sosa_list_by_gen = array(); |
288
|
|
|
|
289
|
|
|
if($gen){ |
290
|
|
|
if(!isset($this->sosa_list_by_gen[$gen])){ |
291
|
|
|
$this->sosa_list_by_gen[$gen] = Database::prepare( |
292
|
|
|
'SELECT majs_sosa AS sosa, majs_i_id AS indi'. |
293
|
|
|
' FROM `##maj_sosa`'. |
294
|
|
|
' WHERE majs_gedcom_id=:tree_id AND majs_user_id=:user_id'. |
295
|
|
|
' AND majs_gen = :gen'. |
296
|
|
|
' ORDER BY majs_sosa ASC') |
297
|
|
|
->execute(array( |
298
|
|
|
'tree_id' => $this->tree->getTreeId(), |
299
|
|
|
'user_id' => $this->user->getUserId(), |
300
|
|
|
'gen' => $gen |
301
|
|
|
)) |
302
|
|
|
->fetchAssoc(); |
303
|
|
|
} |
304
|
|
|
return $this->sosa_list_by_gen[$gen]; |
305
|
|
|
} |
306
|
|
|
return array(); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* Get an associative array of Sosa families in generation G. Keys are Sosa numbers for the husband, values families. |
311
|
|
|
* |
312
|
|
|
* @param number $gen Generation |
313
|
|
|
* @return array Array of Sosa families |
314
|
|
|
*/ |
315
|
|
|
public function getFamilySosaListAtGeneration($gen){ |
316
|
|
|
if(!$this->is_setup) return array(); |
317
|
|
|
if(!$this->sosa_fam_list_by_gen) |
318
|
|
|
$this->sosa_fam_list_by_gen = array(); |
|
|
|
|
319
|
|
|
|
320
|
|
|
if($gen){ |
321
|
|
|
if(!isset($this->sosa_fam_list_by_gen[$gen])){ |
322
|
|
|
$this->sosa_fam_list_by_gen[$gen] = Database::prepare( |
323
|
|
|
'SELECT s1.majs_sosa AS sosa, f_id AS fam'. |
324
|
|
|
' FROM `##families`'. |
325
|
|
|
' INNER JOIN `##maj_sosa` AS s1 ON (`##families`.f_husb = s1.majs_i_id AND `##families`.f_file = s1.majs_gedcom_id)'. |
326
|
|
|
' INNER JOIN `##maj_sosa` AS s2 ON (`##families`.f_wife = s2.majs_i_id AND `##families`.f_file = s2.majs_gedcom_id)'. |
327
|
|
|
' WHERE s1.majs_sosa + 1 = s2.majs_sosa'. |
328
|
|
|
' AND s1.majs_gedcom_id= :tree_id AND s1.majs_user_id=:user_id'. |
329
|
|
|
' AND s2.majs_gedcom_id= :tree_id AND s2.majs_user_id=:user_id'. |
330
|
|
|
' AND s1.majs_gen = :gen'. |
331
|
|
|
' ORDER BY s1.majs_sosa ASC' |
332
|
|
|
) |
333
|
|
|
->execute(array( |
334
|
|
|
'tree_id' => $this->tree->getTreeId(), |
335
|
|
|
'user_id' => $this->user->getUserId(), |
336
|
|
|
'gen' => $gen |
337
|
|
|
)) |
338
|
|
|
->fetchAssoc(); |
339
|
|
|
} |
340
|
|
|
return $this->sosa_fam_list_by_gen[$gen]; |
341
|
|
|
} |
342
|
|
|
return array(); |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* Get an associative array of Sosa individuals in generation G who are missing parents. Keys are Sosa numbers, values individuals. |
347
|
|
|
* |
348
|
|
|
* @param number $gen Generation |
349
|
|
|
* @return array Array of Sosa individuals |
350
|
|
|
*/ |
351
|
|
|
public function getMissingSosaListAtGeneration($gen){ |
352
|
|
|
if(!$this->is_setup) return array(); |
353
|
|
|
if($gen){ |
354
|
|
|
return $this->sosa_list_by_gen[$gen] = Database::prepare( |
355
|
|
|
'SELECT schild.majs_sosa sosa, schild.majs_i_id indi, sfat.majs_sosa IS NOT NULL has_father, smot.majs_sosa IS NOT NULL has_mother'. |
356
|
|
|
' FROM `##maj_sosa` schild'. |
357
|
|
|
' LEFT JOIN `##maj_sosa` sfat ON ((schild.majs_sosa * 2) = sfat.majs_sosa AND schild.majs_gedcom_id = sfat.majs_gedcom_id AND schild.majs_user_id = sfat.majs_user_id)'. |
358
|
|
|
' LEFT JOIN `##maj_sosa` smot ON ((schild.majs_sosa * 2 + 1) = smot.majs_sosa AND schild.majs_gedcom_id = smot.majs_gedcom_id AND schild.majs_user_id = smot.majs_user_id)'. |
359
|
|
|
' WHERE schild.majs_gedcom_id = :tree_id AND schild.majs_user_id = :user_id'. |
360
|
|
|
' AND schild.majs_gen = :gen'. |
361
|
|
|
' AND (sfat.majs_sosa IS NULL OR smot.majs_sosa IS NULL)'. |
362
|
|
|
' ORDER BY schild.majs_sosa ASC') |
363
|
|
|
->execute(array( |
364
|
|
|
'tree_id' => $this->tree->getTreeId(), |
365
|
|
|
'user_id' => $this->user->getUserId(), |
366
|
|
|
'gen' => $gen - 1 |
367
|
|
|
))->fetchAll(\PDO::FETCH_ASSOC); |
368
|
|
|
} |
369
|
|
|
return array(); |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
|
373
|
|
|
|
374
|
|
|
/************* |
375
|
|
|
* STATISTICS |
376
|
|
|
*************/ |
377
|
|
|
/** |
378
|
|
|
* Get the statistic array detailed by generation. |
379
|
|
|
* Statistics for each generation are: |
380
|
|
|
* - The number of Sosa in generation |
381
|
|
|
* - The number of Sosa up to generation |
382
|
|
|
* - The number of distinct Sosa up to generation |
383
|
|
|
* - The year of the first birth in generation |
384
|
|
|
* - The year of the last birth in generation |
385
|
|
|
* - The average year of birth in generation |
386
|
|
|
* |
387
|
|
|
* @return array Statistics array |
388
|
|
|
*/ |
389
|
|
|
public function getStatisticsByGeneration() { |
390
|
|
|
if(!$this->is_setup) return array(); |
391
|
|
|
if(!$this->statistics_tab) { |
|
|
|
|
392
|
|
|
$this->statistics_tab = array(); |
393
|
|
|
if($maxGeneration = $this->getLastGeneration()) { |
394
|
|
|
for ($gen = 1; $gen <= $maxGeneration; $gen++) { |
395
|
|
|
$birthStats = $this->getStatsBirthYearInGeneration($gen); |
396
|
|
|
$this->statistics_tab[$gen] = array( |
397
|
|
|
'sosaCount' => $this->getSosaCountAtGeneration($gen), |
398
|
|
|
'sosaTotalCount' => $this->getSosaCountUpToGeneration($gen), |
399
|
|
|
'diffSosaTotalCount' => $this->getDifferentSosaCountUpToGeneration($gen), |
400
|
|
|
'firstBirth' => $birthStats['first'], |
401
|
|
|
'lastBirth' => $birthStats['last'], |
402
|
|
|
'avgBirth' => $birthStats['avg'] |
403
|
|
|
); |
404
|
|
|
} |
405
|
|
|
} |
406
|
|
|
} |
407
|
|
|
return $this->statistics_tab; |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
/** |
411
|
|
|
* How many individuals exist in the tree. |
412
|
|
|
* |
413
|
|
|
* @return int |
414
|
|
|
*/ |
415
|
|
|
public function getTotalIndividuals() { |
416
|
|
|
if(!$this->is_setup) return 0; |
417
|
|
|
return Database::prepare( |
418
|
|
|
'SELECT SQL_CACHE COUNT(*) FROM `##individuals`' . |
419
|
|
|
' WHERE i_file = :tree_id') |
420
|
|
|
->execute(array('tree_id' => $this->tree->getTreeId())) |
421
|
|
|
->fetchOne() ?: 0; |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
/** |
425
|
|
|
* Get the total Sosa count for all generations |
426
|
|
|
* |
427
|
|
|
* @return number Number of Sosas |
428
|
|
|
*/ |
429
|
|
View Code Duplication |
public function getSosaCount(){ |
|
|
|
|
430
|
|
|
if(!$this->is_setup) return 0; |
431
|
|
|
return Database::prepare( |
432
|
|
|
'SELECT SQL_CACHE COUNT(majs_sosa) FROM `##maj_sosa`' . |
433
|
|
|
' WHERE majs_gedcom_id=:tree_id AND majs_user_id=:user_id') |
434
|
|
|
->execute(array( |
435
|
|
|
'tree_id' => $this->tree->getTreeId(), |
436
|
|
|
'user_id' => $this->user->getUserId() |
437
|
|
|
))->fetchOne() ?: 0; |
438
|
|
|
} |
439
|
|
|
|
440
|
|
|
/** |
441
|
|
|
* Get the number of Sosa in a specific generation. |
442
|
|
|
* |
443
|
|
|
* @param number $gen Generation |
444
|
|
|
* @return number Number of Sosas in generation |
445
|
|
|
*/ |
446
|
|
View Code Duplication |
public function getSosaCountAtGeneration($gen){ |
|
|
|
|
447
|
|
|
if(!$this->is_setup) return 0; |
448
|
|
|
return Database::prepare( |
449
|
|
|
'SELECT SQL_CACHE COUNT(majs_sosa) FROM `##maj_sosa`' . |
450
|
|
|
' WHERE majs_gedcom_id=:tree_id AND majs_user_id=:user_id'. |
451
|
|
|
' AND majs_gen= :gen') |
452
|
|
|
->execute(array( |
453
|
|
|
'tree_id' => $this->tree->getTreeId(), |
454
|
|
|
'user_id' => $this->user->getUserId(), |
455
|
|
|
'gen' => $gen |
456
|
|
|
))->fetchOne() ?: 0; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
/** |
460
|
|
|
* Get the total number of Sosa up to a specific generation. |
461
|
|
|
* |
462
|
|
|
* @param number $gen Generation |
463
|
|
|
* @return number Total number of Sosas up to generation |
464
|
|
|
*/ |
465
|
|
View Code Duplication |
public function getSosaCountUpToGeneration($gen){ |
|
|
|
|
466
|
|
|
if(!$this->is_setup) return 0; |
467
|
|
|
return Database::prepare( |
468
|
|
|
'SELECT SQL_CACHE COUNT(majs_sosa) FROM `##maj_sosa`' . |
469
|
|
|
' WHERE majs_gedcom_id=:tree_id AND majs_user_id=:user_id'. |
470
|
|
|
' AND majs_gen <= :gen') |
471
|
|
|
->execute(array( |
472
|
|
|
'tree_id' => $this->tree->getTreeId(), |
473
|
|
|
'user_id' => $this->user->getUserId(), |
474
|
|
|
'gen' => $gen |
475
|
|
|
))->fetchOne() ?: 0; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* Get the total number of distinct Sosa individual for all generations. |
480
|
|
|
* |
481
|
|
|
* @return number Total number of distinct individual |
482
|
|
|
*/ |
483
|
|
View Code Duplication |
public function getDifferentSosaCount(){ |
|
|
|
|
484
|
|
|
if(!$this->is_setup) return 0; |
485
|
|
|
return Database::prepare( |
486
|
|
|
'SELECT SQL_CACHE COUNT(DISTINCT majs_i_id) FROM `##maj_sosa`' . |
487
|
|
|
' WHERE majs_gedcom_id=:tree_id AND majs_user_id=:user_id') |
488
|
|
|
->execute(array( |
489
|
|
|
'tree_id' => $this->tree->getTreeId(), |
490
|
|
|
'user_id' => $this->user->getUserId() |
491
|
|
|
))->fetchOne() ?: 0; |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
/** |
495
|
|
|
* Get the number of distinct Sosa individual up to a specific generation. |
496
|
|
|
* |
497
|
|
|
* @param number $gen Generation |
498
|
|
|
* @return number Number of distinct Sosa individuals up to generation |
499
|
|
|
*/ |
500
|
|
View Code Duplication |
public function getDifferentSosaCountUpToGeneration($gen){ |
|
|
|
|
501
|
|
|
if(!$this->is_setup) return 0; |
502
|
|
|
return Database::prepare( |
503
|
|
|
'SELECT SQL_CACHE COUNT(DISTINCT majs_i_id) FROM `##maj_sosa`' . |
504
|
|
|
' WHERE majs_gedcom_id=:tree_id AND majs_user_id=:user_id'. |
505
|
|
|
' AND majs_gen <= :gen') |
506
|
|
|
->execute(array( |
507
|
|
|
'tree_id' => $this->tree->getTreeId(), |
508
|
|
|
'user_id' => $this->user->getUserId(), |
509
|
|
|
'gen' => $gen |
510
|
|
|
))->fetchOne() ?: 0; |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
/** |
514
|
|
|
* Get an array of birth statistics for a specific generation |
515
|
|
|
* Statistics are : |
516
|
|
|
* - first : First birth year in generation |
517
|
|
|
* - last : Last birth year in generation |
518
|
|
|
* - avg : Average birth year |
519
|
|
|
* |
520
|
|
|
* @param number $gen Generation |
521
|
|
|
* @return array Birth statistics array |
522
|
|
|
*/ |
523
|
|
|
public function getStatsBirthYearInGeneration($gen){ |
524
|
|
|
if(!$this->is_setup) return array('first' => 0, 'avg' => 0, 'last' => 0); |
525
|
|
|
return Database::prepare( |
526
|
|
|
'SELECT MIN(majs_birth_year) AS first, AVG(majs_birth_year) AS avg, MAX(majs_birth_year) AS last'. |
527
|
|
|
' FROM `##maj_sosa`' . |
528
|
|
|
' WHERE majs_gedcom_id=:tree_id AND majs_user_id=:user_id'. |
529
|
|
|
' AND majs_gen=:gen AND NOT majs_birth_year = :birth_year') |
530
|
|
|
->execute(array( |
531
|
|
|
'tree_id' => $this->tree->getTreeId(), |
532
|
|
|
'user_id' => $this->user->getUserId(), |
533
|
|
|
'gen' => $gen, |
534
|
|
|
'birth_year' => 0)) |
535
|
|
|
->fetchOneRow(\PDO::FETCH_ASSOC) ?: array('first' => 0, 'avg' => 0, 'last' => 0); |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
/** |
539
|
|
|
* Get the mean generation time, based on a linear regression of birth years and generations |
540
|
|
|
* |
541
|
|
|
* @return number|NULL Mean generation time |
542
|
|
|
*/ |
543
|
|
|
public function getMeanGenerationTime(){ |
544
|
|
|
if(!$this->is_setup) return; |
545
|
|
|
if(!$this->statistics_tab){ |
|
|
|
|
546
|
|
|
$this->getStatisticsByGeneration(); |
547
|
|
|
} |
548
|
|
|
//Linear regression on x=generation and y=birthdate |
549
|
|
|
$sum_xy = 0; |
550
|
|
|
$sum_x=0; |
551
|
|
|
$sum_y=0; |
552
|
|
|
$sum_x2=0; |
553
|
|
|
$n=count($this->statistics_tab); |
554
|
|
|
foreach($this->statistics_tab as $gen=>$stats){ |
555
|
|
|
$sum_xy+=$gen*$stats['avgBirth']; |
556
|
|
|
$sum_x+=$gen; |
557
|
|
|
$sum_y+=$stats['avgBirth']; |
558
|
|
|
$sum_x2+=$gen*$gen; |
559
|
|
|
} |
560
|
|
|
$denom=($n*$sum_x2)-($sum_x*$sum_x); |
561
|
|
|
if($denom!=0){ |
562
|
|
|
return -(($n*$sum_xy)-($sum_x*$sum_y))/($denom); |
563
|
|
|
} |
564
|
|
|
return null; |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
/** |
568
|
|
|
* Return a computed array of statistics about the dispersion of ancestors across the ancestors |
569
|
|
|
* at a specified generation. |
570
|
|
|
* This statistics cannot be used for generations above 11, as it would cause a out of range in MySQL |
571
|
|
|
* |
572
|
|
|
* Format: |
573
|
|
|
* - key : a base-2 representation of the ancestor at generation G for which exclusive ancestors have been found, |
574
|
|
|
* -1 is used for shared ancestors |
575
|
|
|
* For instance base2(0100) = base10(4) represent the maternal grand father |
576
|
|
|
* - values: number of ancestors exclusively in the ancestors of the ancestor in key |
577
|
|
|
* |
578
|
|
|
* For instance a result at generation 3 could be : |
579
|
|
|
* array ( -1 => 12 -> 12 ancestors are shared by the grand-parents |
580
|
|
|
* base10(1) => 32 -> 32 ancestors are exclusive to the paternal grand-father |
581
|
|
|
* base10(2) => 25 -> 25 ancestors are exclusive to the paternal grand-mother |
582
|
|
|
* base10(4) => 12 -> 12 ancestors are exclusive to the maternal grand-father |
583
|
|
|
* base10(8) => 30 -> 30 ancestors are exclusive to the maternal grand-mother |
584
|
|
|
* ) |
585
|
|
|
* |
586
|
|
|
* @param int $gen Reference generation |
587
|
|
|
* @return array |
588
|
|
|
*/ |
589
|
|
|
public function getAncestorDispersionForGen($gen) { |
590
|
|
|
if(!$this->is_setup || $gen > 11) return array(); // Going further than 11 gen will be out of range in the query |
591
|
|
|
return Database::prepare( |
592
|
|
|
'SELECT branches, count(i_id)'. |
593
|
|
|
' FROM ('. |
594
|
|
|
' SELECT i_id,'. |
595
|
|
|
' CASE'. |
596
|
|
|
' WHEN CEIL(LOG2(SUM(branch))) = LOG2(SUM(branch)) THEN SUM(branch)'. |
597
|
|
|
' ELSE -1'. // We put all ancestors shared between some branches in the same bucket |
598
|
|
|
' END branches'. |
599
|
|
|
' FROM ('. |
600
|
|
|
' SELECT DISTINCT majs_i_id i_id,'. |
601
|
|
|
' POW(2, FLOOR(majs_sosa / POW(2, (majs_gen - :gen))) - POW(2, :gen -1)) branch'. |
602
|
|
|
' FROM `##maj_sosa`'. |
603
|
|
|
' WHERE majs_gedcom_id = :tree_id AND majs_user_id = :user_id'. |
604
|
|
|
' AND majs_gen >= :gen'. |
605
|
|
|
' ) indistat'. |
606
|
|
|
' GROUP BY i_id'. |
607
|
|
|
') grouped'. |
608
|
|
|
' GROUP BY branches') |
609
|
|
|
->execute(array( |
610
|
|
|
'tree_id' => $this->tree->getTreeId(), |
611
|
|
|
'user_id' => $this->user->getUserId(), |
612
|
|
|
'gen' => $gen |
613
|
|
|
))->fetchAssoc() ?: array(); |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
|
617
|
|
|
} |
618
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.