]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Util/FakeData.php
Handle sequence on PostgreSQL for titles; closes #1374 refs #1158
[galette.git] / galette / lib / Galette / Util / FakeData.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Generates fake data as example
7 *
8 * PHP version 5
9 *
10 * Copyright © 2017-2018 The Galette Team
11 *
12 * This file is part of Galette (http://galette.tuxfamily.org).
13 *
14 * Galette is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * Galette is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
26 *
27 * @category Util
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2017-2018 The Galette Team
32 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
33 * @link http://galette.tuxfamily.org
34 * @since Available since 0.9
35 */
36
37 namespace Galette\Util;
38
39 use Analog\Analog;
40 use Galette\Core\Db;
41 use Galette\Core\I18n;
42 use Galette\Core\Preferences;
43 use Galette\Core\History;
44 use Galette\Core\Login;
45 use Galette\Entity\Adherent;
46 use Galette\Entity\Contribution;
47 use Galette\Repository\Titles;
48 use Galette\Entity\Status;
49 use Galette\Entity\ContributionsTypes;
50 use Galette\Entity\Group;
51 use Galette\Entity\Transaction;
52 use Galette\Entity\PaymentType;
53
54 /**
55 * Generate random data
56 *
57 * @category Util
58 * @name FakeData
59 * @package Galette
60 * @author Johan Cwiklinski <johan@x-tnd.be>
61 * @copyright 2017 The Galette Team
62 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
63 * @link http://galette.tuxfamily.org
64 * @see https://github.com/fzaninotto/Faker
65 * @since Available since 0.9dev - 2017-02-20
66 */
67 class FakeData
68 {
69 const DEFAULT_NB_MEMBERS = 20;
70 const DEFAULT_NB_CONTRIB = 5;
71 const DEFAULT_NB_GROUPS = 5;
72 const DEFAULT_NB_TRANSACTIONS = 2;
73 const DEFAULT_PHOTOS = false;
74
75 protected $preferences;
76 protected $member_fields;
77 protected $history;
78 protected $login;
79
80 protected $zdb;
81 protected $i18n;
82 protected $faker;
83 private $report = [
84 'success' => [],
85 'errors' => [],
86 'warnings' => []
87 ];
88
89 protected $groups = [];
90 protected $mids = [];
91 protected $transactions = [];
92 protected $titles = [];
93 protected $status;
94 protected $contrib_types;
95
96 /**
97 * @var integer
98 * Number of members to generate
99 */
100 protected $nbmembers = self::DEFAULT_NB_MEMBERS;
101
102 /**
103 * @var boolean
104 * With members photos
105 */
106 protected $with_photos = self::DEFAULT_PHOTOS;
107
108 /**
109 * @var integer
110 * Number of groups to generate
111 */
112 protected $nbgroups = self::DEFAULT_NB_GROUPS;
113
114 /**
115 * @var integer
116 * Max number of contributions to generate
117 * for each member
118 */
119 protected $maxcontribs = self::DEFAULT_NB_CONTRIB;
120
121 /**
122 * @var integer
123 * Number of transactions to generate
124 */
125 protected $nbtransactions = self::DEFAULT_NB_TRANSACTIONS;
126
127 /**
128 * @var integer
129 * Seed to use for data generation (to get same data accross runs)
130 */
131 protected $seed;
132
133 /**
134 * Default constructor
135 *
136 * @param Db $zdb Db instance
137 * @param I18n $i18n Current language
138 * @param boolean $generate Process data generation; defaults to false
139 *
140 * @return void
141 */
142 public function __construct(Db $zdb, I18n $i18n, $generate = false)
143 {
144 $this->zdb = $zdb;
145 $this->i18n = $i18n;
146 if ($generate) {
147 $this->generate();
148 }
149 }
150
151 /**
152 * Set seed
153 *
154 * @param integer $seed Seed
155 *
156 * @return FakeData
157 */
158 public function setSeed($seed)
159 {
160 $this->seed = $seed;
161 return $this;
162 }
163
164 /**
165 * Set number of members to generate
166 *
167 * @param integer $nb Number of members
168 *
169 * @return FakeData
170 */
171 public function setNbMembers($nb)
172 {
173 $this->nbmembers = (int)$nb;
174 return $this;
175 }
176
177 /**
178 * Set maximum number of contribution per member to generate
179 *
180 * @param integer $nb Number of contributions
181 *
182 * @return FakeData
183 */
184 public function setMaxContribs($nb)
185 {
186 $this->maxcontribs = (int)$nb;
187 return $this;
188 }
189
190 /**
191 * Set number of groups to generate
192 *
193 * @param integer $nb Number of groups
194 *
195 * @return FakeData
196 */
197 public function setNbGroups($nb)
198 {
199 $this->nbgroups = (int)$nb;
200 return $this;
201 }
202
203 /**
204 * Set number of transactions to generate
205 *
206 * @param integer $nb Number of transactions
207 *
208 * @return FakeData
209 */
210 public function setNbTransactions($nb)
211 {
212 $this->nbtransactions = (int)$nb;
213 return $this;
214 }
215
216 /**
217 * Set with members photos or not
218 *
219 * @param boolean $with With photos
220 *
221 * @return FakeData
222 */
223 public function setWithPhotos($with)
224 {
225 $this->with_photos = $with;
226 return $this;
227 }
228
229 /**
230 * Get (and create if needed) Faker instance
231 *
232 * @return \Faker\Factory
233 */
234 private function getFaker()
235 {
236 if ($this->faker === null) {
237 $this->faker = \Faker\Factory::create($this->i18n->getID());
238 }
239 return $this->faker;
240 }
241
242 /**
243 * Do data generation
244 *
245 * @return void
246 */
247 public function generate()
248 {
249 $this->getFaker();
250 if ($this->seed !== null) {
251 $this->faker->seed($this->seed);
252 }
253
254 $this->generateGroups($this->nbgroups);
255 $this->generateMembers($this->nbmembers);
256 $this->generateTransactions($this->nbtransactions);
257 $this->generateContributions();
258 }
259
260 /**
261 * Generate groups
262 *
263 * @param integer $count Number of groups to generate
264 *
265 * @return void
266 */
267 public function generateGroups($count = null)
268 {
269 $faker = $this->getFaker();
270
271 $done = 0;
272 $parent_group = null;
273
274 if ($count === null) {
275 $count = $this->nbgroups;
276 }
277
278 for ($i = 0; $i < $count; $i++) {
279 $group = new Group();
280 $group->setName($faker->unique()->lastName());
281 if (count($this->groups) > 0 && $faker->boolean($chanceOfGettingTrue = 10)) {
282 if ($parent_group === null) {
283 $parent_group = $faker->randomElement($this->groups);
284 }
285 $group->setParentGroup($parent_group->getId());
286 }
287
288 if ($group->store()) {
289 $this->groups[] = $group;
290 ++$done;
291 }
292 }
293
294 if ($count !=0 && $done != 0) {
295 if ($done === $count) {
296 $this->addSuccess(
297 str_replace('%count', $count, _T("%count groups created"))
298 );
299 } else {
300 $this->addWarning(
301 str_replace(
302 ['%count', '%done'],
303 [$count, $done],
304 _T("%count groups requested, and %done created")
305 )
306 );
307 }
308 }
309 }
310
311 /**
312 * Generate members
313 *
314 * @param integer $count Number of members to generate
315 *
316 * @return void
317 */
318 public function generateMembers($count = null)
319 {
320 $faker = $this->getFaker();
321 $done = 0;
322 $photos_done = 0;
323
324 if ($count === null) {
325 $count = $this->nbmembers;
326 }
327
328 for ($i = 0; $i < $count; $i++) {
329 $data = $this->fakeMember();
330
331 $member = new Adherent($this->zdb);
332 $member->setDependencies(
333 $this->preferences,
334 $this->member_fields,
335 $this->history
336 );
337 if ($member->check($data, [], [])) {
338 if ($member->store()) {
339 $this->mids[] = $member->id;
340 ++$done;
341 if ($this->with_photos && $faker->boolean($chanceOfGettingTrue = 70)) {
342 if ($this->addPhoto($member)) {
343 ++$photos_done;
344 }
345 }
346 }
347
348 //add to a group?
349 if (count($this->groups) > 0 && $faker->boolean($chanceOfGettingTrue = 60)) {
350 $groups = $faker->randomElements($this->groups);
351 foreach ($groups as $group) {
352 $manager = $faker->boolean($chanceOfGettingTrue = 10);
353 if ($manager) {
354 $managers = $group->getManagers();
355 $managers[] = $member;
356 $group->setManagers($managers);
357 } else {
358 $members = $group->getMembers();
359 $members[] = $member;
360 $group->setMembers($members);
361 }
362 }
363 }
364 }
365 }
366
367 if ($count !=0 && $done != 0) {
368 if ($done === $count) {
369 $this->addSuccess(
370 str_replace('%count', $count, _T("%count members created"))
371 );
372 } else {
373 $this->addWarning(
374 str_replace(
375 ['%count', '%done'],
376 [$count, $done],
377 _T("%count members requested, and %done created")
378 )
379 );
380 }
381 }
382 if ($this->with_photos === true) {
383 if ($photos_done > 0) {
384 $this->addSuccess(
385 str_replace('%count', $count, _T("%count photos created"))
386 );
387 } else {
388 $this->addWarning(
389 _T("No photo has been created")
390 );
391 }
392 }
393 }
394
395 /**
396 * Get faked member data
397 *
398 * @return array
399 */
400 public function fakeMember()
401 {
402 $faker = $this->getFaker();
403 if ($this->seed !== null) {
404 $this->faker->seed($this->seed);
405 }
406 $creation_date = $faker->dateTimeBetween($startDate = '-3 years', $endDate = 'now');
407 $mdp_adh = $faker->password();
408
409 if ($this->status === null) {
410 $status = new Status($this->zdb);
411 $this->status = array_keys($status->getList());
412 }
413
414 $data= [
415 'nom_adh' => $faker->lastName(),
416 'prenom_adh' => $faker->firstName(),
417 'ville_adh' => $faker->city(),
418 'cp_adh' => $faker->postcode(),
419 'adresse_adh' => $faker->streetAddress(),
420 'ville_adh' => $faker->city(),
421 'email_adh' => $faker->unique()->email(),
422 'login_adh' => $faker->unique()->userName(),
423 'mdp_adh' => $mdp_adh,
424 'mdp_adh2' => $mdp_adh,
425 'bool_admin_adh' => $faker->boolean($chanceOfGettingTrue = 5),
426 'bool_exempt_adh' => $faker->boolean($chanceOfGettingTrue = 5),
427 'bool_display_info' => $faker->boolean($chanceOfGettingTrue = 70),
428 'sexe_adh' => $faker->randomElement([Adherent::NC, Adherent::MAN, Adherent::WOMAN]),
429 'prof_adh' => $faker->jobTitle(),
430 'titre_adh' => $faker->randomElement(array_keys($this->titles)),
431 'ddn_adh' => $faker->dateTimeBetween(
432 $startDate = '-110 years',
433 $endDate = date('Y-m-d')
434 )->format(_T("Y-m-d")),
435 'lieu_naissance' => $faker->city(),
436 'pseudo_adh' => $faker->userName(),
437 'adresse_adh' => $faker->streetAddress(),
438 'cp_adh' => $faker->postcode(),
439 'ville_adh' => $faker->city(),
440 'pays_adh' => $faker->optional()->country(),
441 'tel_adh' => $faker->phoneNumber(),
442 'url_adh' => $faker->optional()->url(),
443 'activite_adh' => $faker->boolean($chanceOfGettingTrue = 90),
444 'id_statut' => $faker->optional($weight = 0.3, $default = Status::DEFAULT_STATUS)
445 ->randomElement($this->status),
446 'date_crea_adh' => $creation_date->format(_T("Y-m-d")),
447 'pref_lang' => $faker->randomElement(array_keys($this->i18n->getArrayList())),
448 'fingerprint' => 'FAKER' . ($this->seed !== null ? $this->seed : '')
449 ];
450
451 if ($faker->boolean($chanceOfGettingTrue = 20)) {
452 $data['societe_adh'] = $faker->company();
453 $data['is_company'] = true;
454 }
455
456 return $data;
457 }
458
459 /**
460 * Add photo to a member
461 *
462 * @param Adherent $member Member instance
463 *
464 * @return boolean
465 */
466 public function addPhoto(Adherent $member)
467 {
468 $file = GALETTE_TEMPIMAGES_PATH . 'fakephoto.jpg';
469 if (!defined('GALETTE_TESTS')) {
470 $faker = $this->getFaker();
471 $url = $faker->unique()->imageUrl(
472 $width = 800,
473 $height = 600,
474 'people',
475 true,
476 'Galette fake data'
477 );
478 } else {
479 $url = GALETTE_ROOT . '../tests/fake_image.jpg';
480 }
481
482 if (copy($url, $file)) {
483 $_FILES = array(
484 'photo' => array(
485 'name' => 'fakephoto.jpg',
486 'type' => 'image/jpeg',
487 'size' => filesize($file),
488 'tmp_name' => $file,
489 'error' => 0
490 )
491 );
492 $res = $member->picture->store($_FILES['photo'], true);
493 if ($res < 0) {
494 $this->addError(
495 _T("Photo has not been stored!")
496 );
497 } else {
498 return true;
499 }
500 } else {
501 $this->addError(
502 _T("Photo has not been copied!")
503 );
504 }
505 return false;
506 }
507
508 /**
509 * Generate transactions
510 *
511 * @param integer $count Number of transactions to generate
512 * @param array $mids Members ids. Defaults to null (will work with previously generated ids)
513 *
514 * @return void
515 */
516 public function generateTransactions($count = null, $mids = null)
517 {
518 $faker = $this->getFaker();
519
520 $done = 0;
521
522 if ($count === null) {
523 $count = $this->nbtransactions;
524 }
525
526 if ($mids === null) {
527 $mids = $this->mids;
528 }
529
530 for ($i = 0; $i < $count; $i++) {
531 $data= [
532 'trans_date' => $faker->dateTimeBetween($startDate = '-1 years', $endDate = 'now')->format(_T("Y-m-d")),
533 Adherent::PK => $faker->randomElement($mids),
534 'trans_amount' => $faker->numberBetween($min = 50, $max = 2000),
535 'trans_desc' => $faker->realText($maxNbChars = 150)
536 ];
537
538 $transaction = new Transaction($this->zdb, $this->login);
539 if ($transaction->check($data, [], [])) {
540 if ($transaction->store($this->history)) {
541 $this->transactions[] = $transaction;
542 ++$done;
543 }
544 }
545 }
546
547 if ($count !=0 && $done != 0) {
548 if ($done === $count) {
549 $this->addSuccess(
550 str_replace('%count', $count, _T("%count transactions created"))
551 );
552 } else {
553 $this->addWarning(
554 str_replace(
555 ['%count', '%done'],
556 [$count, $done],
557 _T("%count transactions requested, and %done created")
558 )
559 );
560 }
561 }
562 }
563
564 /**
565 * Generate members contributions
566 *
567 * @param array $mids Members ids. Defaults to null (will work with previously generated ids)
568 *
569 * @return void
570 */
571 public function generateContributions($mids = null)
572 {
573 $faker = $this->getFaker();
574
575 if ($this->maxcontribs == 0) {
576 return;
577 }
578
579 if ($mids === null) {
580 $mids = $this->mids;
581 }
582
583 $done = 0;
584
585 foreach ($mids as $mid) {
586 $nbcontribs = $faker->numberBetween(0, $this->maxcontribs);
587 for ($i = 0; $i < $nbcontribs; $i++) {
588 $data = $this->fakeContrib($mid);
589 $contrib = new Contribution($this->zdb, $this->login);
590 if ($contrib->check($data, [], []) === true) {
591 if ($contrib->store()) {
592 $pk = Contribution::PK;
593 $this->cids[] = $contrib->$pk;
594 ++$done;
595 }
596
597 if (count($this->transactions) > 0) {
598 if ($faker->boolean($chanceOfGettingTrue = 90)) {
599 $contrib::setTransactionPart(
600 $this->zdb,
601 $transaction->id,
602 $contrib->id
603 );
604 }
605 }
606 }
607 }
608 }
609
610 if ($done > 0) {
611 $this->addSuccess(
612 str_replace('%count', $done, _T("%count contributions created"))
613 );
614 } else {
615 $this->addError(
616 _T("No contribution created!")
617 );
618 }
619 }
620
621 /**
622 * Get faked contribution data
623 *
624 * @param integer $mid Member id.
625 *
626 * @return array
627 */
628 public function fakeContrib($mid)
629 {
630 $faker = $this->getFaker();
631 if ($this->seed !== null) {
632 $this->faker->seed($this->seed);
633 }
634
635 if ($this->contrib_types === null) {
636 $ct = new ContributionsTypes($this->zdb);
637 $this->contrib_types = $ct->getCompleteList();
638 }
639 $types = $this->contrib_types;
640
641 $begin_date = $faker->dateTimeBetween($startDate = '-3 years', $endDate = 'now');
642 $end_date = clone $begin_date;
643 $end_date->modify('+1 year');
644 if (!$begin_date) {
645 $begin_date = new \DateTime();
646 }
647
648 $data = [
649 Adherent::PK => $mid,
650 ContributionsTypes::PK => $faker->randomElement(array_keys($types)),
651 'montant_cotis' => $faker->numberBetween($min = 5, $max = 200),
652 'type_paiement_cotis' => $faker->randomElement(
653 [
654 PaymentType::OTHER,
655 PaymentType::CASH,
656 PaymentType::CREDITCARD,
657 PaymentType::CHECK,
658 PaymentType::TRANSFER,
659 PaymentType::PAYPAL
660 ]
661 ),
662 'info_cotis' => ($this->seed !== null ?
663 'FAKER' . $this->seed :
664 $faker->optional($weight = 0.1)->realText($maxNbChars = 500)),
665 'date_enreg' => $faker->dateTimeBetween($startDate = '-1 years', $endDate = 'now')->format(_T("Y-m-d")),
666 'date_debut_cotis' => $begin_date->format(_T("Y-m-d")),
667 'date_fin_cotis' => $end_date->format(_T("Y-m-d"))
668 ];
669
670 if (count($this->transactions) > 0) {
671 if ($faker->boolean($chanceOfGettingTrue = 90)) {
672 $transaction = $faker->randomElement($this->transactions);
673 $missing = $transaction->getMissingAmount();
674 if ($data['montant_cotis'] > $missing) {
675 $data['montant_cotis'] = $missing;
676 }
677 }
678 }
679
680 return $data;
681 }
682
683 /**
684 * Add success message
685 *
686 * @param string $msg Message
687 *
688 * @return void
689 */
690 protected function addSuccess($msg)
691 {
692 $this->report['success'][] = $msg;
693 }
694
695 /**
696 * Add error message
697 *
698 * @param string $msg Message
699 *
700 * @return void
701 */
702 protected function addError($msg)
703 {
704 $this->report['errors'][] = $msg;
705 }
706
707 /**
708 * Add warning message
709 *
710 * @param string $msg Message
711 *
712 * @return void
713 */
714 protected function addWarning($msg)
715 {
716 $this->report['warnings'][] = $msg;
717 }
718
719 /**
720 * Get report
721 *
722 * @return array
723 */
724 public function getReport()
725 {
726 return $this->report;
727 }
728
729 /**
730 * Set dependencies
731 *
732 * @param Preferences $preferences Preferences instance
733 * @param array $fields Members fields configuration
734 * @param History $history History instance
735 * @param Login $login Login instance
736 *
737 * @return FakeData
738 */
739 public function setDependencies(
740 Preferences $preferences,
741 array $fields,
742 History $history,
743 Login $login
744 ) {
745 $this->preferences = $preferences;
746 $this->member_fields = $fields;
747 $this->history = $history;
748 $this->login = $login;
749
750 return $this;
751 }
752
753 /**
754 * Get generated members ids
755 *
756 * @return array
757 */
758 public function getMembersIds()
759 {
760 return $this->mids;
761 }
762 }