From 5ddc0e7a53b390b6e176798b65afdab7ab5ade13 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 22 Jun 2021 15:31:49 +0200 Subject: [PATCH 01/24] Simplify loading of Symfony commands. --- .../Command/ChillPersonMoveCommand.php | 71 +-- .../Command/ImportPeopleFromCSVCommand.php | 484 ++++++++---------- .../ChillPersonExtension.php | 1 - .../ChillPersonBundle/config/services.yaml | 7 + .../config/services/command.yaml | 19 - 5 files changed, 246 insertions(+), 336 deletions(-) delete mode 100644 src/Bundle/ChillPersonBundle/config/services/command.yaml diff --git a/src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php b/src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php index 4f2b2098a..b68997ed6 100644 --- a/src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php +++ b/src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php @@ -1,25 +1,7 @@ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ namespace Chill\PersonBundle\Command; -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -28,39 +10,28 @@ use Doctrine\ORM\EntityManagerInterface; use Chill\PersonBundle\Entity\Person; use Symfony\Component\Console\Exception\RuntimeException; use Psr\Log\LoggerInterface; +use Symfony\Component\Console\Command\Command; -class ChillPersonMoveCommand extends ContainerAwareCommand +final class ChillPersonMoveCommand extends Command { - /** - * - * @var PersonMove - */ - protected $mover; - - /** - * - * @var EntityManagerInterface - */ - protected $em; - - /** - * - * @var LoggerInterface - */ - protected $chillLogger; - + private PersonMove $mover; + + private EntityManagerInterface $em; + + private LoggerInterface $chillLogger; + public function __construct( - PersonMove $mover, + PersonMove $mover, EntityManagerInterface $em, LoggerInterface $chillLogger ) { parent::__construct('chill:person:move'); - + $this->mover = $mover; $this->em = $em; $this->chillLogger = $chillLogger; } - + protected function configure() { $this @@ -73,14 +44,14 @@ class ChillPersonMoveCommand extends ContainerAwareCommand ->addOption('delete-entity', null, InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, "entity to delete", []) ; } - + protected function interact(InputInterface $input, OutputInterface $output) { if (FALSE === $input->hasOption('dump-sql') && FALSE === $input->hasOption('force')) { $msg = "You must use \"--dump-sql\" or \"--force\""; throw new RuntimeException($msg); } - + foreach (["from", "to"] as $name) { if (empty($input->getOption($name))) { throw new RuntimeException("You must set a \"$name\" option"); @@ -90,7 +61,7 @@ class ChillPersonMoveCommand extends ContainerAwareCommand throw new RuntimeException("The id in \"$name\" field does not contains " . "only digits: $id"); } - } + } } protected function execute(InputInterface $input, OutputInterface $output) @@ -99,16 +70,16 @@ class ChillPersonMoveCommand extends ContainerAwareCommand $from = $repository->find($input->getOption('from')); $to = $repository->find($input->getOption('to')); $deleteEntities = $input->getOption('delete-entity'); - + if ($from === NULL) { throw new RuntimeException(sprintf("Person \"from\" with id %d not found", $input->getOption('from'))); } if ($to === NULL) { throw new RuntimeException(sprintf("Person \"to\" with id %d not found", $input->getOption('to'))); } - + $sqls = $this->mover->getSQL($from, $to, $deleteEntities); - + if ($input->getOption('dump-sql')) { foreach($sqls as $sql) { $output->writeln($sql); @@ -125,25 +96,25 @@ class ChillPersonMoveCommand extends ContainerAwareCommand $connection->executeQuery($sql); } $connection->commit(); - + $this->chillLogger->notice("Move a person from command line succeeded", $ctxt); } } - + protected function buildLoggingContext(Person $from, Person $to, $deleteEntities, $sqls) { $ctxt = [ 'from' => $from->getId(), 'to' => $to->getId() ]; - + foreach ($deleteEntities as $key => $de) { $ctxt['delete_entity_'.$key] = $de; } foreach ($sqls as $key => $sql) { $ctxt['sql_'.$key] = $sql; } - + return $ctxt; } diff --git a/src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php b/src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php index d5e4c597c..35f596295 100644 --- a/src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php +++ b/src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php @@ -1,22 +1,5 @@ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - namespace Chill\PersonBundle\Command; use Chill\MainBundle\Templating\TranslatableStringHelper; @@ -40,65 +23,37 @@ use Symfony\Component\Console\Question\ConfirmationQuestion; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\Form\FormFactory; +use Symfony\Component\Form\FormFactoryInterface; -/** - * Class ImportPeopleFromCSVCommand - * - * @package Chill\PersonBundle\Command - * @author Julien Fastré - */ -class ImportPeopleFromCSVCommand extends Command +final class ImportPeopleFromCSVCommand extends Command { + private InputInterface $input; + + private OutputInterface $output; + + private LoggerInterface $logger; + + private TranslatableStringHelper $helper; + + private EntityManagerInterface $em; + + private EventDispatcherInterface $eventDispatcher; + /** - * @var InputInterface + * The line currently read */ - protected $input; - + private int $line; + /** - * @var OutputInterface + * Where key are column names, and value the custom field slug */ - protected $output; - - /** - * @var \Psr\Log\LoggerInterface - */ - protected $logger; - - /** - * @var \Chill\MainBundle\Templating\TranslatableStringHelper - */ - protected $helper; - - /** - * @var \Doctrine\Persistence\ObjectManager - */ - protected $em; - - /** - * @var EventDispatcherInterface - */ - protected $eventDispatcher; - - /** - * the line currently read - * - * @var int - */ - protected $line; - - /** - * @var array where key are column names, and value the custom field slug - */ - protected $customFieldMapping = array(); - - /** - * @var CustomFieldProvider - */ - protected $customFieldProvider; - + private array $customFieldMapping = []; + + private CustomFieldProvider $customFieldProvider; + /** * Contains an array of information searched in the file. - * + * * position 0: the information key (which will be used in this process) * position 1: the helper * position 2: the default value @@ -121,19 +76,16 @@ class ImportPeopleFromCSVCommand extends Command ['locality', 'The column header for locality', 'locality'], ['center', 'The column header for center', 'center'] ); - + /** * Different possible format to interpret a date * * @var string */ protected static $defaultDateInterpreter = "%d/%m/%Y|%e/%m/%y|%d/%m/%Y|%e/%m/%Y"; - - /** - * @var FormFactory - */ - protected $formFactory; - + + private FormFactoryInterface $formFactory; + /** * ImportPeopleFromCSVCommand constructor. * @@ -150,7 +102,7 @@ class ImportPeopleFromCSVCommand extends Command EntityManagerInterface $em, CustomFieldProvider $customFieldProvider, EventDispatcherInterface $eventDispatcher, - FormFactory $formFactory + FormFactoryInterface $formFactory ) { $this->logger = $logger; $this->helper = $helper; @@ -158,10 +110,10 @@ class ImportPeopleFromCSVCommand extends Command $this->customFieldProvider = $customFieldProvider; $this->eventDispatcher = $eventDispatcher; $this->formFactory = $formFactory; - + parent::__construct('chill:person:import'); } - + /** * */ @@ -171,10 +123,10 @@ class ImportPeopleFromCSVCommand extends Command ->addArgument('csv_file', InputArgument::REQUIRED, "The CSV file to import") ->setDescription("Import people from a csv file") ->setHelp(<<addArgument('locale', InputArgument::REQUIRED, + ->addArgument('locale', InputArgument::REQUIRED, "The locale to use in displaying translatable strings from entities") ->addOption( - 'force-center', - null, + 'force-center', + null, InputOption::VALUE_REQUIRED, "The id of the center" ) @@ -197,10 +149,10 @@ EOF "Persist people in the database (default is not to persist people)" ) ->addOption( - 'delimiter', - 'd', - InputOption::VALUE_OPTIONAL, - "The delimiter character of the csv file", + 'delimiter', + 'd', + InputOption::VALUE_OPTIONAL, + "The delimiter character of the csv file", ",") ->addOption( 'enclosure', @@ -236,33 +188,33 @@ EOF "The path of the file to load the matching between label in CSV and answers" ) ; - + // mapping columns foreach (self::$mapping as $m) { $this->addOptionShortcut($m[0], $m[1], $m[2]); } - + // other information $this->addOptionShortcut('birthdate_format', 'Format preference for ' - . 'birthdate. See help for date formats preferences.', + . 'birthdate. See help for date formats preferences.', self::$defaultDateInterpreter); $this->addOptionShortcut('opening_date_format', 'Format preference for ' . 'opening date. See help for date formats preferences.', self::$defaultDateInterpreter); $this->addOptionShortcut('closing_date_format', 'Format preference for ' - . 'closing date. See help for date formats preferences.', + . 'closing date. See help for date formats preferences.', self::$defaultDateInterpreter); - + // mapping column to custom fields - $this->addOption('custom-field', NULL, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + $this->addOption('custom-field', NULL, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, "Mapping a column to a custom fields key. Example: 1=cf_slug"); $this->addOption('skip-interactive-field-mapping', null, InputOption::VALUE_NONE, "Do not ask for interactive mapping"); } - + /** * This function is a shortcut to addOption. - * + * * @param string $name * @param string $description * @param string $default @@ -271,10 +223,10 @@ EOF protected function addOptionShortcut($name, $description, $default) { $this->addOption($name, null, InputOption::VALUE_OPTIONAL, $description, $default); - + return $this; } - + /** * @param InputInterface $input * @param OutputInterface $output @@ -285,17 +237,17 @@ EOF $this->input = $input; $this->output = $output; $this->logger = new ConsoleLogger($output); - + $csv = $this->openCSV(); - + // getting the first row if (($row = fgetcsv( - $csv, - $input->getOption('length'), - $input->getOption('delimiter'), - $input->getOption('enclosure'), + $csv, + $input->getOption('length'), + $input->getOption('delimiter'), + $input->getOption('enclosure'), $input->getOption('escape'))) !== false) { - + try { $this->matchColumnToCustomField($row); } finally { @@ -303,33 +255,33 @@ EOF fclose($csv); } } - + // load the matching between csv and label $this->loadAnswerMatching(); } - + /** * @param $row */ protected function matchColumnToCustomField($row) { - + $cfMappingsOptions = $this->input->getOption('custom-field'); /* @var $em \Doctrine\Persistence\ObjectManager */ $em = $this->em; - + foreach($cfMappingsOptions as $cfMappingStringOption) { list($rowNumber, $cfSlug) = preg_split('|=|', $cfMappingStringOption); - + // check that the column exists, getting the column name $column = $row[$rowNumber]; - + if (empty($column)) { $message = "The column with row $rowNumber is empty."; $this->logger->error($message); throw new \RuntimeException($message); } - + // check a custom field exists try { $customField = $em->createQuery("SELECT cf " @@ -357,15 +309,15 @@ EOF . "Stopping this command."); throw new \RuntimeException("The custom field with slug $cfSlug could not be found. " . "Stopping this command."); - } - + } + $this->logger->notice(sprintf("Matched custom field %s (question : '%s') on column %d (displayed in the file as '%s')", $customField->getSlug(), $this->helper->localize($customField->getName()), $rowNumber, $column)); - + $this->customFieldMapping[$rowNumber] = $customField; } } - + /** * Load the mapping between answer in CSV and value in choices from a json file */ @@ -383,7 +335,7 @@ EOF } } } - + /** * */ @@ -392,14 +344,14 @@ EOF if ($this->input->hasOption('dump-choice-matching') && !empty($this->input->getOption('dump-choice-matching'))) { $this->logger->debug("Dump the matching between answer and choices"); $str = json_encode($this->cacheAnswersMapping, JSON_PRETTY_PRINT); - + $fs = new Filesystem(); $filename = $this->input->getOption('dump-choice-matching'); - + $fs->dumpFile($filename, $str); } } - + /** * @param InputInterface $input * @param OutputInterface $output @@ -410,22 +362,22 @@ EOF { $this->input = $input; $this->output = $output; - + $this->logger->debug("Setting locale to ".$input->getArgument('locale')); setlocale(LC_TIME, $input->getArgument('locale')); - + // opening csv as resource $csv = $this->openCSV(); - + $num = 0; $line = $this->line = 1; - + try { while (($row = fgetcsv( - $csv, - $input->getOption('length'), - $input->getOption('delimiter'), - $input->getOption('enclosure'), + $csv, + $input->getOption('length'), + $input->getOption('delimiter'), + $input->getOption('enclosure'), $input->getOption('escape'))) !== false) { $this->logger->debug("Processing line ".$this->line); if ($line === 1 ) { @@ -434,11 +386,11 @@ EOF $headers = $this->processingHeaders($row); } else { $person = $this->createPerson($row, $headers); - + if (count($this->customFieldMapping) > 0) { $this->processingCustomFields($person, $row); } - + $event = new Event(); $event->person = $person; $event->rawHeaders = $rawHeaders; @@ -449,21 +401,21 @@ EOF $event->input = $this->input; $event->output = $this->output; $event->helperSet = $this->getHelperSet(); - + $this->eventDispatcher->dispatch('chill_person.person_import', $event); - - if ($this->input->getOption('force') === TRUE + + if ($this->input->getOption('force') === TRUE && $event->skipPerson === false) { $this->em->persist($person); } - + $num ++; } $line ++; $this->line++; } - + if ($this->input->getOption('force') === true) { $this->logger->debug('persisting entitites'); $this->em->flush(); @@ -475,9 +427,9 @@ EOF $this->dumpAnswerMatching(); } } - + /** - * + * * @return resource * @throws \RuntimeException */ @@ -485,23 +437,23 @@ EOF { $fs = new Filesystem(); $filename = $this->input->getArgument('csv_file'); - + if (!$fs->exists($filename)) { throw new \RuntimeException("The file does not exists or you do not " . "have the right to read it."); } - + $resource = fopen($filename, 'r'); - + if ($resource == FALSE) { throw new \RuntimeException("The file '$filename' could not be opened."); } - + return $resource; } - + /** - * + * * @param type $firstRow * @return array where keys are column number, and value is information mapped */ @@ -510,11 +462,11 @@ EOF $availableOptions = array_map(function($m) { return $m[0]; }, self::$mapping); $matchedColumnHeaders = array(); $headers = array(); - + foreach($availableOptions as $option) { $matchedColumnHeaders[$option] = $this->input->getOption($option); } - + foreach($firstRow as $key => $content) { $content = trim($content); if (in_array($content, $matchedColumnHeaders)) { @@ -524,11 +476,11 @@ EOF } else { $this->logger->notice("Column with content '$content' is ignored"); } - } - + } + return $headers; } - + /** * * @param array $row @@ -541,21 +493,21 @@ EOF // trying to get the opening date $openingDateString = trim($row[array_search('opening_date', $headers)]); $openingDate = $this->processDate($openingDateString, $this->input->getOption('opening_date_format')); - + $person = $openingDate instanceof \DateTime ? new Person($openingDate) : new Person(); // add the center $center = $this->getCenter($row, $headers); - + if ($center === null) { throw new \Exception("center not found"); } - + $person->setCenter($center); - + foreach($headers as $column => $info) { - + $value = trim($row[$column]); - + switch($info) { case 'firstname': $person->setFirstName($value); @@ -582,12 +534,12 @@ EOF $person->setEmail($value); break; case 'phonenumber': - $person->setPhonenumber($value); + $person->setPhonenumber($value); break; case 'mobilenumber': $person->setMobilenumber($value); break; - + // we just keep the column number for those data case 'postalcode': $postalCodeValue = $value; @@ -600,33 +552,33 @@ EOF break; } } - + // handle address if (\in_array('postalcode', $headers)) { - + if (! empty($postalCodeValue)) { - + $address = new Address(); $postalCode = $this->guessPostalCode($postalCodeValue, $localityValue ?? ''); - + if ($postalCode === null) { throw new \Exception("The locality is not found"); } - + $address->setPostcode($postalCode); - + if (\in_array('street1', $headers)) { $address->setStreetAddress1($street1Value); } $address->setValidFrom(new \DateTime('today')); - + $person->addAddress($address); } } - + return $person; } - + /** * @param $row * @param $headers @@ -640,7 +592,7 @@ EOF } else { $columnCenter = \array_search('center', $headers); $centerName = \trim($row[$columnCenter]); - + try { return $this->em->createQuery('SELECT c FROM ChillMainBundle:Center c ' . 'WHERE c.name = :center_name') @@ -654,7 +606,7 @@ EOF } } } - + /** * @param $centerName * @return Center|mixed|null|object @@ -664,68 +616,68 @@ EOF if (!\array_key_exists('_center_picked', $this->cacheAnswersMapping)) { $this->cacheAnswersMapping['_center_picked'] = []; } - + if (\array_key_exists($centerName, $this->cacheAnswersMapping['_center_picked'])) { $id = $this->cacheAnswersMapping['_center_picked'][$centerName]; - + return $this->em->getRepository(Center::class) ->find($id); } - + $centers = $this->em->createQuery("SELECT c FROM ChillMainBundle:Center c " . "ORDER BY SIMILARITY(c.name, :center_name) DESC") ->setParameter('center_name', $centerName) ->setMaxResults(10) ->getResult() ; - + if (count($centers) > 1) { if (\strtolower($centers[0]->getName()) === \strtolower($centerName)) { return $centers[0]; } } - + $centersByName = []; - $names = \array_map(function(Center $c) use (&$centersByName) { + $names = \array_map(function(Center $c) use (&$centersByName) { $n = $c->getName(); $centersByName[$n] = $c; return $n; - + }, $centers); $names[] = "none of them"; - + $helper = $this->getHelper('question'); - $question = new ChoiceQuestion(sprintf("Which center match the name \"%s\" ? (default to \"%s\")", $centerName, $names[0]), + $question = new ChoiceQuestion(sprintf("Which center match the name \"%s\" ? (default to \"%s\")", $centerName, $names[0]), $names, 0); - + $answer = $helper->ask($this->input, $this->output, $question); - + if ($answer === 'none of them') { $questionCreate = new ConfirmationQuestion("Would you like to create it ?", false); $create = $helper->ask($this->input, $this->output, $questionCreate); - + if ($create) { $center = (new Center()) ->setName($centerName) ; - + if ($this->input->getOption('force') === TRUE) { $this->em->persist($center); $this->em->flush(); } - + return $center; } } - + $center = $centersByName[$answer]; - + $this->cacheAnswersMapping['_center_picked'][$centerName] = $center->getId(); - + return $center; } - + /** * @param $postalCode * @param $locality @@ -736,15 +688,15 @@ EOF if (!\array_key_exists('_postal_code_picked', $this->cacheAnswersMapping)) { $this->cacheAnswersMapping['_postal_code_picked'] = []; } - + if (\array_key_exists($postalCode, $this->cacheAnswersMapping['_postal_code_picked'])) { if (\array_key_exists($locality, $this->cacheAnswersMapping['_postal_code_picked'][$postalCode])) { $id = $this->cacheAnswersMapping['_postal_code_picked'][$postalCode][$locality]; - + return $this->em->getRepository(PostalCode::class)->find($id); } } - + $postalCodes = $this->em->createQuery("SELECT pc FROM ".PostalCode::class." pc " . "WHERE pc.code = :postal_code " . "ORDER BY SIMILARITY(pc.name, :locality) DESC " @@ -754,48 +706,48 @@ EOF ->setParameter('locality', $locality) ->getResult() ; - + if (count($postalCodes) >= 1) { - if ($postalCodes[0]->getCode() === $postalCode + if ($postalCodes[0]->getCode() === $postalCode && $postalCodes[0]->getName() === $locality) { return $postalCodes[0]; } } - + if (count($postalCodes) === 0) { return null; } - + $postalCodeByName = []; $names = \array_map(function(PostalCode $pc) use (&$postalCodeByName) { $n = $pc->getName(); $postalCodeByName[$n] = $pc; - + return $n; }, $postalCodes); $names[] = 'none of them'; - + $helper = $this->getHelper('question'); $question = new ChoiceQuestion(sprintf("Which postal code match the " - . "name \"%s\" with postal code \"%s\" ? (default to \"%s\")", - $locality, $postalCode, $names[0]), + . "name \"%s\" with postal code \"%s\" ? (default to \"%s\")", + $locality, $postalCode, $names[0]), $names, 0); - + $answer = $helper->ask($this->input, $this->output, $question); - + if ($answer === 'none of them') { return null; } - + $pc = $postalCodeByName[$answer]; - - $this->cacheAnswersMapping['_postal_code_picked'][$postalCode][$locality] = + + $this->cacheAnswersMapping['_postal_code_picked'][$postalCode][$locality] = $pc->getId(); - + return $pc; } - + /** * @param Person $person * @param $value @@ -804,29 +756,29 @@ EOF protected function processBirthdate(Person $person, $value) { if (empty($value)) { return; } - + $date = $this->processDate($value, $this->input->getOption('birthdate_format')); - + if ($date instanceof \DateTime) { // we correct birthdate if the date is in the future // the most common error is to set date 100 years to late (ex. 2063 instead of 1963) if ($date > new \DateTime('yesterday')) { $date = $date->sub(new \DateInterval('P100Y')); } - + $person->setBirthdate($date); - + return; } - + // if we arrive here, we could not process the date $this->logger->warning(sprintf( "Line %d : the birthdate could not be interpreted. Was %s.", $this->line, $value)); - + } - + /** * @param Person $person * @param $value @@ -835,39 +787,39 @@ EOF protected function processClosingDate(Person $person, $value) { if (empty($value)) { return; } - + // we skip if the opening date is now (or after yesterday) /* @var $period \Chill\PersonBundle\Entity\AccompanyingPeriod */ $period = $person->getCurrentAccompanyingPeriod(); - + if ($period->getOpeningDate() > new \DateTime('yesterday')) { $this->logger->debug(sprintf("skipping a closing date because opening date is after yesterday (%s)", $period->getOpeningDate()->format('Y-m-d'))); return; } - - + + $date = $this->processDate($value, $this->input->getOption('closing_date_format')); - + if ($date instanceof \DateTime) { // we correct birthdate if the date is in the future // the most common error is to set date 100 years to late (ex. 2063 instead of 1963) if ($date > new \DateTime('yesterday')) { $date = $date->sub(new \DateInterval('P100Y')); } - + $period->setClosingDate($date); $person->close(); return; } - + // if we arrive here, we could not process the date $this->logger->warning(sprintf( "Line %d : the closing date could not be interpreted. Was %s.", $this->line, $value)); } - + /** * @param Person $person * @param $row @@ -875,27 +827,27 @@ EOF */ protected function processingCustomFields(Person $person, $row) { - + /* @var $cfProvider \Chill\CustomFieldsBundle\Service\CustomFieldProvider */ $cfProvider = $this->customFieldProvider; $cfData = array(); - + /* @var $$customField \Chill\CustomFieldsBundle\Entity\CustomField */ foreach($this->customFieldMapping as $rowNumber => $customField) { $builder = $this->formFactory->createBuilder(); $cfProvider->getCustomFieldByType($customField->getType()) ->buildForm($builder, $customField); $form = $builder->getForm(); - + // get the type of the form $type = get_class($form->get($customField->getSlug()) ->getConfig()->getType()->getInnerType()); - $this->logger->debug(sprintf("Processing a form of type %s", + $this->logger->debug(sprintf("Processing a form of type %s", $type)); switch ($type) { case \Symfony\Component\Form\Extension\Core\Type\TextType::class: - $cfData[$customField->getSlug()] = + $cfData[$customField->getSlug()] = $this->processTextType($row[$rowNumber], $form, $customField); break; case \Symfony\Component\Form\Extension\Core\Type\ChoiceType::class: @@ -903,12 +855,12 @@ EOF $cfData[$customField->getSlug()] = $this->processChoiceType($row[$rowNumber], $form, $customField); } - + } - + $person->setCFData($cfData); } - + /** * Process a text type on a custom field * @@ -917,24 +869,24 @@ EOF * @return type */ protected function processTextType( - $value, - \Symfony\Component\Form\FormInterface $form, + $value, + \Symfony\Component\Form\FormInterface $form, \Chill\CustomFieldsBundle\Entity\CustomField $cf ) { $form->submit(array($cf->getSlug() => $value)); - + $value = $form->getData()[$cf->getSlug()]; - + $this->logger->debug(sprintf("Found value : %s for custom field with question " . "'%s'", $value, $this->helper->localize($cf->getName()))); - + return $value; } - + protected $cacheAnswersMapping = array(); - - + + /** * Process a custom field choice. * @@ -949,14 +901,14 @@ EOF */ protected function processChoiceType( $value, - \Symfony\Component\Form\FormInterface $form, + \Symfony\Component\Form\FormInterface $form, \Chill\CustomFieldsBundle\Entity\CustomField $cf ) { // getting the possible answer and their value : $view = $form->get($cf->getSlug())->createView(); $answers = $this->collectChoicesAnswers($view->vars['choices']); - + // if we do not have any answer on the question, throw an error. if (count($answers) === 0) { $message = sprintf( @@ -964,34 +916,34 @@ EOF $this->helper->localize($cf->getName()), $cf->getSlug() ); - + $this->logger->error($message, array( 'method' => __METHOD__, 'slug' => $cf->getSlug(), 'question' => $this->helper->localize($cf->getName()) )); - + throw new \RuntimeException($message); } - + if ($view->vars['required'] === false) { $answers[null] = '** no answer'; } - + // the answer does not exists in cache. Try to find it, or asks the user if (!isset($this->cacheAnswersMapping[$cf->getSlug()][$value])) { - + // try to find the answer (with array_keys and a search value $values = array_keys( - array_map(function($label) { return trim(strtolower($label)); }, $answers), + array_map(function($label) { return trim(strtolower($label)); }, $answers), trim(strtolower($value)), true ); - + if (count($values) === 1) { // we could guess an answer ! $this->logger->info("This question accept multiple answers"); - $this->cacheAnswersMapping[$cf->getSlug()][$value] = + $this->cacheAnswersMapping[$cf->getSlug()][$value] = $view->vars['multiple'] == false ? $values[0] : array($values[0]); $this->logger->info(sprintf("Guessed that value '%s' match with key '%s' " . "because the CSV and the label are equals.", @@ -1021,20 +973,20 @@ EOF array_keys($matchingTableRowAnswer) ); $question->setErrorMessage("This choice is not possible"); - + if ($view->vars['multiple']) { $this->logger->debug("this question is multiple"); $question->setMultiselect(true); } - + $selected = $this->getHelper('question')->ask($this->input, $this->output, $question); - $this->output->writeln(sprintf('You have selected "%s"', - is_array($answers[$matchingTableRowAnswer[$selected]]) ? + $this->output->writeln(sprintf('You have selected "%s"', + is_array($answers[$matchingTableRowAnswer[$selected]]) ? implode(',', $answers[$matchingTableRowAnswer[$selected]]) : $answers[$matchingTableRowAnswer[$selected]]) ); - + // recording value in cache $this->cacheAnswersMapping[$cf->getSlug()][$value] = $matchingTableRowAnswer[$selected]; $this->logger->debug(sprintf("Setting the value '%s' in cache for customfield '%s' and answer '%s'", @@ -1045,19 +997,19 @@ EOF $value)); } } - + $form->submit(array($cf->getSlug() => $this->cacheAnswersMapping[$cf->getSlug()][$value])); $value = $form->getData()[$cf->getSlug()]; - + $this->logger->debug(sprintf( - "Found value : %s for custom field with question '%s'", - is_array($value) ? implode(',', $value) : $value, + "Found value : %s for custom field with question '%s'", + is_array($value) ? implode(',', $value) : $value, $this->helper->localize($cf->getName())) ); - + return $value; } - + /** * Recursive method to collect the possibles answer from a ChoiceType (or * its inherited types). @@ -1069,7 +1021,7 @@ EOF private function collectChoicesAnswers($choices) { $answers = array(); - + /* @var $choice \Symfony\Component\Form\ChoiceList\View\ChoiceView */ foreach($choices as $choice) { if ($choice instanceof \Symfony\Component\Form\ChoiceList\View\ChoiceView) { @@ -1085,10 +1037,10 @@ EOF )); } } - + return $answers; } - + /** * @param $value * @param $formats @@ -1097,13 +1049,13 @@ EOF protected function processDate($value, $formats) { $possibleFormats = explode("|", $formats); - + foreach($possibleFormats as $format) { $this->logger->debug("Trying format $format", array(__METHOD__)); $dateR = strptime($value, $format); - + if (is_array($dateR) && $dateR['unparsed'] === '') { - $string = sprintf("%04d-%02d-%02d %02d:%02d:%02d", + $string = sprintf("%04d-%02d-%02d %02d:%02d:%02d", ($dateR['tm_year']+1900), ($dateR['tm_mon']+1), ($dateR['tm_mday']), @@ -1112,19 +1064,19 @@ EOF ($dateR['tm_sec'])); $date = \DateTime::createFromFormat("Y-m-d H:i:s", $string); $this->logger->debug(sprintf("Interpreting %s as date %s", $value, $date->format("Y-m-d H:i:s"))); - + return $date; } } - + // if we arrive here, we could not process the date $this->logger->debug(sprintf( "Line %d : a date could not be interpreted. Was %s.", $this->line, $value)); - + return false; } - - + + } diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php index 79dae3255..f52564cfa 100644 --- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php +++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php @@ -69,7 +69,6 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac $loader->load('services/search.yaml'); $loader->load('services/menu.yaml'); $loader->load('services/privacyEvent.yaml'); - $loader->load('services/command.yaml'); $loader->load('services/actions.yaml'); $loader->load('services/form.yaml'); $loader->load('services/alt_names.yaml'); diff --git a/src/Bundle/ChillPersonBundle/config/services.yaml b/src/Bundle/ChillPersonBundle/config/services.yaml index 7f70a02dd..463ad0141 100644 --- a/src/Bundle/ChillPersonBundle/config/services.yaml +++ b/src/Bundle/ChillPersonBundle/config/services.yaml @@ -12,6 +12,13 @@ services: tags: - { name: 'serializer.normalizer', priority: 64 } + Chill\PersonBundle\Command\: + resource: '../Command/' + autowire: true + autoconfigure: true + tags: + - { name: console.command } + chill.person.form.type.select2maritalstatus: class: Chill\PersonBundle\Form\Type\Select2MaritalStatusType arguments: diff --git a/src/Bundle/ChillPersonBundle/config/services/command.yaml b/src/Bundle/ChillPersonBundle/config/services/command.yaml deleted file mode 100644 index 685a348dd..000000000 --- a/src/Bundle/ChillPersonBundle/config/services/command.yaml +++ /dev/null @@ -1,19 +0,0 @@ -services: - Chill\PersonBundle\Command\ChillPersonMoveCommand: - arguments: - $em: '@Doctrine\ORM\EntityManagerInterface' - $mover: '@Chill\PersonBundle\Actions\Remove\PersonMove' - $chillLogger: '@chill.main.logger' - tags: - - { name: console.command } - - Chill\PersonBundle\Command\ImportPeopleFromCSVCommand: - arguments: - $logger: '@logger' - $helper: '@Chill\MainBundle\Templating\TranslatableStringHelper' - $em: '@Doctrine\ORM\EntityManagerInterface' - $customFieldProvider: '@Chill\CustomFieldsBundle\Service\CustomFieldProvider' - $eventDispatcher: '@Symfony\Component\EventDispatcher\EventDispatcherInterface' - $formFactory: '@form.factory' - tags: - - { name: console.command } From 2667867bd042167863e1a49fbade006bfacfb985 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 23 Jun 2021 22:28:05 +0200 Subject: [PATCH 02/24] Update composer - Bump league/php. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index fb0d2dcd1..b5af76421 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "knplabs/knp-time-bundle": "^1.12", "symfony/intl": "4.*", "symfony/swiftmailer-bundle": "^3.5", - "league/csv": "^9.6", + "league/csv": "^9.7.1", "phpoffice/phpspreadsheet": "^1.16", "symfony/browser-kit": "^5.2", "symfony/css-selector": "^5.2", From dc26ca7e7708e5ccb42ba800c06d57bc7f82e1ac Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 23 Jun 2021 16:39:40 +0200 Subject: [PATCH 03/24] Add missing getId() method. --- src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php index 996fcf3eb..633df147c 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php @@ -46,6 +46,11 @@ class Goal $this->results = new ArrayCollection(); } + public function getId(): int + { + return $this->id; + } + public function getTitle(): array { return $this->title; From df1f44d81453f1a60b3a46d9fa32f5264ec9fe7b Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 23 Jun 2021 16:40:04 +0200 Subject: [PATCH 04/24] Let SocialAction::defaultNotificationDelay be nullable. --- .../Entity/SocialWork/SocialAction.php | 2 +- .../migrations/Version20210623142046.php | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillPersonBundle/migrations/Version20210623142046.php diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php index 35abe392f..506a736d8 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php @@ -47,7 +47,7 @@ class SocialAction private $children; /** - * @ORM\Column(type="dateinterval") + * @ORM\Column(type="dateinterval", nullable=true) */ private $defaultNotificationDelay; diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210623142046.php b/src/Bundle/ChillPersonBundle/migrations/Version20210623142046.php new file mode 100644 index 000000000..8baffb867 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210623142046.php @@ -0,0 +1,29 @@ +addSql('ALTER TABLE chill_person_social_action ALTER defaultnotificationdelay DROP NOT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_person_social_action ALTER defaultNotificationDelay SET NOT NULL'); + } +} From 6e37e7feacd22f0c7721cc934274f0c57b287f04 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Wed, 23 Jun 2021 16:40:43 +0200 Subject: [PATCH 05/24] Let repositories implements ObjectRepository. --- .../SocialWork/EvaluationRepository.php | 39 ++++++++++++++++++- .../Repository/SocialWork/GoalRepository.php | 38 +++++++++++++++++- .../SocialWork/ResultRepository.php | 38 +++++++++++++++++- .../SocialWork/SocialActionRepository.php | 37 +++++++++++++++++- .../SocialWork/SocialIssueRepository.php | 29 ++++++-------- 5 files changed, 160 insertions(+), 21 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php index f554e4e8c..d0f11c2d3 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php @@ -5,8 +5,9 @@ namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\Persistence\ObjectRepository; -final class EvaluationRepository +final class EvaluationRepository implements ObjectRepository { private EntityRepository $repository; @@ -14,4 +15,40 @@ final class EvaluationRepository { $this->repository = $entityManager->getRepository(Evaluation::class); } + + public function find($id, ?int $lockMode = null, ?int $lockVersion = null): ?Evaluation + { + return $this->repository->find($id, $lockMode, $lockVersion); + } + + /** + * @return array + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array + */ + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria, ?array $orderBy = null): ?Evaluation + { + return $this->repository->findOneBy($criteria, $orderBy); + } + + /** + * @return class-string + */ + public function getClassName(): string + { + return Evaluation::class; + } + + } diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php index bd7c6a738..cb2815d89 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php @@ -5,8 +5,9 @@ namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\Goal; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\Persistence\ObjectRepository; -final class GoalRepository +final class GoalRepository implements ObjectRepository { private EntityRepository $repository; @@ -14,4 +15,39 @@ final class GoalRepository { $this->repository = $entityManager->getRepository(Goal::class); } + + public function find($id, ?int $lockMode = null, ?int $lockVersion = null): ?Goal + { + return $this->repository->find($id, $lockMode, $lockVersion); + } + + /** + * @return array + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array + */ + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria, ?array $orderBy = null): ?Goal + { + return $this->repository->findOneBy($criteria, $orderBy); + } + + /** + * @return class-string + */ + public function getClassName(): string + { + return Goal::class; + } + } diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php index 1c4d8d013..047e4325f 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php @@ -5,8 +5,9 @@ namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\Result; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\Persistence\ObjectRepository; -final class ResultRepository +final class ResultRepository implements ObjectRepository { private EntityRepository $repository; @@ -14,4 +15,39 @@ final class ResultRepository { $this->repository = $entityManager->getRepository(Result::class); } + + public function find($id, ?int $lockMode = null, ?int $lockVersion = null): ?Result + { + return $this->repository->find($id, $lockMode, $lockVersion); + } + + /** + * @return array + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array + */ + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria, ?array $orderBy = null): ?Result + { + return $this->repository->findOneBy($criteria, $orderBy); + } + + /** + * @return class-string + */ + public function getClassName(): string + { + return Result::class; + } + } diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php index 3c86d2397..a81789c0a 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php @@ -5,8 +5,9 @@ namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\Persistence\ObjectRepository; -final class SocialActionRepository +final class SocialActionRepository implements ObjectRepository { private EntityRepository $repository; @@ -14,4 +15,38 @@ final class SocialActionRepository { $this->repository = $entityManager->getRepository(SocialAction::class); } + + public function find($id, ?int $lockMode = null, ?int $lockVersion = null): ?SocialAction + { + return $this->repository->find($id, $lockMode, $lockVersion); + } + + /** + * @return array + */ + public function findAll(): array + { + return $this->repository->findAll(); + } + + /** + * @return array + */ + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria, ?array $orderBy = null): ?SocialAction + { + return $this->repository->findOneBy($criteria, $orderBy); + } + + /** + * @return class-string + */ + public function getClassName(): string + { + return SocialAction::class; + } } diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php index 4dc5ecc67..b0324ba33 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php @@ -1,9 +1,10 @@ repository = $entityManager->getRepository(SocialIssue::class); } - /** - * {@inheritDoc} - */ - public function find($id) + public function find($id, ?int $lockMode = null, ?int $lockVersion = null): ?SocialIssue { - return $this->repository->find($id); + return $this->repository->find($id, $lockMode, $lockVersion); } /** - * {@inheritDoc} + * @return array */ - public function findAll() + public function findAll(): array { return $this->repository->findAll(); } /** - * {@inheritDoc} + * @return array */ - public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null) + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array { return $this->repository->findBy($criteria, $orderBy, $limit, $offset); } - /** - * {@inheritDoc} - */ - public function findOneBy(array $criteria) + public function findOneBy(array $criteria, ?array $orderBy = null): ?SocialIssue { - return $this->findOneBy($criteria); + return $this->repository->findOneBy($criteria, $orderBy); } /** - * {@inheritDoc} + * @return class-string */ - public function getClassName() + public function getClassName(): string { return SocialIssue::class; } From 141aabcddccd1a80f731ccae8d125a20ff5a8ef8 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 22 Jun 2021 15:40:49 +0200 Subject: [PATCH 06/24] feat: Add new command. --- .../Command/ImportSocialWorkMetadata.php | 63 ++++ .../Service/Import/ChillImporter.php | 10 + .../Service/Import/SocialWorkMetadata.php | 289 ++++++++++++++++++ .../Import/SocialWorkMetadataInterface.php | 9 + .../ChillPersonBundle/config/services.yaml | 7 +- 5 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillPersonBundle/Command/ImportSocialWorkMetadata.php create mode 100644 src/Bundle/ChillPersonBundle/Service/Import/ChillImporter.php create mode 100644 src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php create mode 100644 src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadataInterface.php diff --git a/src/Bundle/ChillPersonBundle/Command/ImportSocialWorkMetadata.php b/src/Bundle/ChillPersonBundle/Command/ImportSocialWorkMetadata.php new file mode 100644 index 000000000..e42f0ef26 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Command/ImportSocialWorkMetadata.php @@ -0,0 +1,63 @@ +importer = $socialWorkMetadata; + } + + protected function configure() + { + $this + ->setName('chill:person:import-socialwork') + ->addOption('filepath', 'f', InputOption::VALUE_REQUIRED, 'The file to import.') + ->addOption('language', 'l', InputOption::VALUE_OPTIONAL, 'The default language'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $filepath = $input->getOption('filepath'); + + try { + $csv = Reader::createFromPath($filepath); + } catch (Throwable $e) { + throw new Exception('Error while loading CSV.',0, $e); + } + + $csv->setDelimiter(';'); + + return true === $this->importer->import($csv) ? + 0: + 1; + } +} diff --git a/src/Bundle/ChillPersonBundle/Service/Import/ChillImporter.php b/src/Bundle/ChillPersonBundle/Service/Import/ChillImporter.php new file mode 100644 index 000000000..4360a7cfa --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Service/Import/ChillImporter.php @@ -0,0 +1,10 @@ +socialIssueRepository = $socialIssueRepository; + $this->socialActionRepository = $socialActionRepository; + $this->goalRepository = $goalRepository; + $this->resultRepository = $resultRepository; + $this->evaluationRepository = $evaluationRepository; + $this->entityManager = $entityManager; + } + + public function import(iterable $dataset): bool + { + foreach ($dataset as $row) { + $this->import1( + array_map( + static fn (string $column): ?string => '' === $column ? null : $column, + array_map('trim', $row) + ) + ); + } + + return true; + } + + private function import1(array $row): void + { + // Structure: + // Index 0: SocialIssue + // Index 1: SocialIssue.children + // Index 2: SocialAction + // Index 3: SocialAction.children + // Index 4: Goal + // Index 5: Result + // Index 6: Evaluation + + $socialIssue = $this->handleSocialIssue($row[0], $row[1]); + + $socialAction = $this->handleSocialAction($row[2], $row[3], $socialIssue); + + $goal = $this->handleGoal($row[4], $socialAction); + + $result = $this->handleResult($row[5], $socialAction, $goal); + + $eval = $this->handleEvaluation($row[6], $socialAction); + + $this->entityManager->flush(); + } + + private function handleSocialIssue(?string $socialIssueTitle = null, ?string $socialIssueChildrenTitle = null): SocialIssue + { + if (null !== $socialIssueChildrenTitle) { + /** @var SocialIssue $socialIssueChildren */ + $socialIssueChildren = $this->getOrCreateEntity($this->socialIssueRepository, 'title', ['fr' => $socialIssueChildrenTitle]); + $socialIssueChildren->setTitle(['fr' => $socialIssueChildrenTitle]); + + $this->entityManager->persist($socialIssueChildren); + } + + /** @var SocialIssue $socialIssue */ + $socialIssue = $this->getOrCreateEntity($this->socialIssueRepository, 'title', ['fr' => $socialIssueTitle]); + $socialIssue->setTitle(['fr' => $socialIssueTitle]); + + if (null !== $socialIssueChildrenTitle) { + $socialIssue->addChild($socialIssueChildren); + } + + $this->entityManager->persist($socialIssue); + + return null === $socialIssueChildrenTitle ? $socialIssue : $socialIssueChildren; + } + + private function handleSocialAction(?string $socialActionTitle, ?string $socialActionChildrenTitle, SocialIssue $socialIssue): SocialAction + { + if (null !== $socialActionChildrenTitle) { + /** @var SocialAction $socialActionChildren */ + $socialActionChildren = $this->getOrCreateEntity($this->socialActionRepository, 'title', ['fr' => $socialActionChildrenTitle]); + $socialActionChildren->setTitle(['fr' => $socialActionChildrenTitle]); + + $this->entityManager->persist($socialActionChildren); + } + + /** @var SocialIssue $socialIssue */ + $socialAction = $this->getOrCreateEntity($this->socialActionRepository, 'title', ['fr' => $socialActionTitle]); + $socialAction->setTitle(['fr' => $socialActionTitle]); + + if (null !== $socialActionChildrenTitle) { + $socialActionChildren->setIssue($socialIssue); + $this->entityManager->persist($socialActionChildren); + + $socialAction->addChild($socialActionChildren); + } else { + $socialAction->setIssue($socialIssue); + } + + $this->entityManager->persist($socialAction); + + return null === $socialActionChildrenTitle ? $socialAction : $socialActionChildren; + } + + private function handleGoal(?string $goalTitle = null, ?SocialAction $socialAction = null): ?Goal + { + if (null === $goalTitle) { + return null; + } + + /** @var Goal $goal */ + $goal = $this->getOrCreateEntity($this->goalRepository, 'title', ['fr' => $goalTitle]); + $goal->setTitle(['fr' => $goalTitle]); + + if (null !== $socialAction) { + $goal->addSocialAction($socialAction); + } + + $this->entityManager->persist($goal); + + return $goal; + } + + private function handleResult(?string $resultTitle = null, SocialAction $socialAction, ?Goal $goal = null): ?Result + { + if (null === $resultTitle) { + return null; + } + + /** @var Result $result */ + $result = $this->getOrCreateEntity($this->resultRepository, 'title', ['fr' => $resultTitle]); + $result->setTitle(['fr' => $resultTitle]); + + if (null !== $goal) { + $result->addGoal($goal); + } + + // Why this cannot be found from the Goal entity? + $result->addSocialAction($socialAction); + + $this->entityManager->persist($result); + + return $result; + } + + private function handleEvaluation(?string $evaluationTitle, SocialAction $socialAction): ?Evaluation + { + if (null === $evaluationTitle) { + return null; + } + + /** @var Evaluation $eval */ + $eval = $this->getOrCreateEntity($this->evaluationRepository, 'title', ['fr' => $evaluationTitle]); + $eval->setTitle(['fr' => $evaluationTitle]); + $eval->setSocialAction($socialAction); + + $this->entityManager->persist($eval); + + return $eval; + } + + private function findByJson(ObjectRepository $repository, string $field, array $jsonCriterias): array + { + $qb = $this + ->entityManager + ->createQueryBuilder() + ->select('s') + ->from($repository->getClassName(), 's'); + + $expr = $qb->expr(); + + $temporaryJsonCriterias = $jsonParameters = []; + + foreach ($jsonCriterias as $key => $value) { + $temporaryJsonCriterias[] = [$field, $key, $value, sprintf(':placeholder_%s_%s', $field, $key)]; + } + + $jsonParameters = array_reduce( + $temporaryJsonCriterias, + static function (array $carry, array $row): array + { + [,, $value, $placeholder] = $row; + + return array_merge( + $carry, + [ + $placeholder => sprintf('"%s"', $value), + ] + ); + }, + [] + ); + + $jsonPredicates = array_map( + static function (array $row) use ($expr): Comparison + { + [$field, $key,, $placeholder] = $row; + + $left = sprintf( + "GET_JSON_FIELD_BY_KEY(s.%s, '%s')", + $field, + $key + ); + + return $expr + ->eq( + $left, + $placeholder + ); + }, + $temporaryJsonCriterias + ); + + $query = $qb + ->select('s') + ->where(...$jsonPredicates) + ->setParameters($jsonParameters) + ->getQuery(); + + return $query->getResult(); + } + + private function getOrCreateEntity(ObjectRepository $repository, string $field, array $jsonCriterias = []) + { + $results = $this + ->findByJson( + $repository, + $field, + $jsonCriterias + ); + + switch (true) { + case count($results) === 0: + $entity = $repository->getClassName(); + $entity = new $entity(); + break; + case count($results) === 1; + $entity = current($results); + break; + case count($results) > 1; + throw new Exception( + sprintf( + 'More than one entity(%s) found.', + $repository->getClassName() + ) + ); + } + + return $entity; + } + + +} diff --git a/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadataInterface.php b/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadataInterface.php new file mode 100644 index 000000000..015ef7485 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadataInterface.php @@ -0,0 +1,9 @@ + Date: Thu, 24 Jun 2021 15:24:14 +0200 Subject: [PATCH 07/24] (to rebase/fixup later) Fields must be nullable for import to success. --- .../Entity/SocialWork/Evaluation.php | 2 +- .../migrations/Version20210624131722.php | 29 +++++++++++++++++++ .../migrations/Version20210624131723.php | 29 +++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillPersonBundle/migrations/Version20210624131722.php create mode 100644 src/Bundle/ChillPersonBundle/migrations/Version20210624131723.php diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php index 5a877e264..e08a6de77 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php @@ -23,7 +23,7 @@ class Evaluation private $title = []; /** - * @ORM\Column(type="dateinterval") + * @ORM\Column(type="dateinterval", nullable=true) */ private $delay; diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210624131722.php b/src/Bundle/ChillPersonBundle/migrations/Version20210624131722.php new file mode 100644 index 000000000..ebeb33e50 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210624131722.php @@ -0,0 +1,29 @@ +addSql('ALTER TABLE chill_person_social_work_evaluation ALTER delay DROP NOT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_person_social_work_evaluation ALTER delay SET NOT NULL'); + } +} diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210624131723.php b/src/Bundle/ChillPersonBundle/migrations/Version20210624131723.php new file mode 100644 index 000000000..d36d23733 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210624131723.php @@ -0,0 +1,29 @@ +addSql('ALTER TABLE chill_person_social_work_evaluation ALTER notificationdelay DROP NOT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE chill_person_social_work_evaluation ALTER notificationdelay SET NOT NULL'); + } +} From 18d0ad67d6fb41d05bb8d07bc504e721bc3b83d1 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Thu, 24 Jun 2021 15:24:29 +0200 Subject: [PATCH 08/24] (to rebase/fixup later) Update command based on feedback. --- .../ChillPersonBundle/Service/Import/SocialWorkMetadata.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php b/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php index 0fff36d0f..b5ade864c 100644 --- a/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php +++ b/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php @@ -149,7 +149,10 @@ final class SocialWorkMetadata implements SocialWorkMetadataInterface $goal->setTitle(['fr' => $goalTitle]); if (null !== $socialAction) { + $socialAction->addGoal($goal); $goal->addSocialAction($socialAction); + + $this->entityManager->persist($socialAction); } $this->entityManager->persist($goal); From 3b5ef53b9b2e176dc08e9eea65315a5d1256063e Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Thu, 24 Jun 2021 15:46:04 +0200 Subject: [PATCH 09/24] (to rebase/fixup later) Let Result be associated to a SocialAction. --- .../Service/Import/SocialWorkMetadata.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php b/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php index b5ade864c..48dff99bc 100644 --- a/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php +++ b/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php @@ -160,7 +160,7 @@ final class SocialWorkMetadata implements SocialWorkMetadataInterface return $goal; } - private function handleResult(?string $resultTitle = null, SocialAction $socialAction, ?Goal $goal = null): ?Result + private function handleResult(?string $resultTitle = null, ?SocialAction $socialAction = null, ?Goal $goal = null): ?Result { if (null === $resultTitle) { return null; @@ -172,12 +172,15 @@ final class SocialWorkMetadata implements SocialWorkMetadataInterface if (null !== $goal) { $result->addGoal($goal); + } else { + $result->addSocialAction($socialAction); } - // Why this cannot be found from the Goal entity? $result->addSocialAction($socialAction); + $socialAction->addResult($result); $this->entityManager->persist($result); + $this->entityManager->persist($socialAction); return $result; } From 4a2ada784a97d7a867d83070e00be6c8934adc17 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Thu, 24 Jun 2021 15:57:03 +0200 Subject: [PATCH 10/24] (to rebase/fixup later) Let Goal be associated to a Result. --- .../ChillPersonBundle/Service/Import/SocialWorkMetadata.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php b/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php index 48dff99bc..0896021f1 100644 --- a/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php +++ b/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php @@ -172,6 +172,9 @@ final class SocialWorkMetadata implements SocialWorkMetadataInterface if (null !== $goal) { $result->addGoal($goal); + $goal->addResult($result); + + $this->entityManager->persist($goal); } else { $result->addSocialAction($socialAction); } From d71c3f310e8b93fd1afabd55052d30927c53c984 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Thu, 24 Jun 2021 16:01:41 +0200 Subject: [PATCH 11/24] (to rebase/fixup later) Fix documentation. --- .../Service/Import/SocialWorkMetadata.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php b/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php index 0896021f1..4683344dd 100644 --- a/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php +++ b/src/Bundle/ChillPersonBundle/Service/Import/SocialWorkMetadata.php @@ -66,10 +66,10 @@ final class SocialWorkMetadata implements SocialWorkMetadataInterface private function import1(array $row): void { // Structure: - // Index 0: SocialIssue - // Index 1: SocialIssue.children - // Index 2: SocialAction - // Index 3: SocialAction.children + // Index 0: SocialIssue.parent + // Index 1: SocialIssue + // Index 2: SocialAction.parent + // Index 3: SocialAction // Index 4: Goal // Index 5: Result // Index 6: Evaluation From 2699d485331a4ec938ca83785b4bb9429cd3a216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 22 Jul 2021 15:20:45 +0000 Subject: [PATCH 12/24] Fix tests after introduction of bootstrap --- .../Resources/views/layout.html.twig | 2 +- .../views/layoutWithVerticalMenu.html.twig | 2 +- .../translations/messages+intl-icu.fr.yaml | 6 +++++ .../ChillPersonBundle/Entity/Person.php | 8 +++++-- .../Resources/views/Person/banner.html.twig | 6 ++--- .../AccompanyingPeriodControllerTest.php | 24 +++++++++---------- .../Controller/PersonControllerUpdateTest.php | 5 ++-- 7 files changed, 31 insertions(+), 22 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml diff --git a/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig b/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig index b06d7db90..75b6831de 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/layout.html.twig @@ -50,7 +50,7 @@
{# Flash messages ! #} - {% if app.session.flashbag.all()|length > 0 %} + {% if app.session.flashbag.keys()|length > 0 %}
{% for flashMessage in app.session.flashbag.get('success') %} diff --git a/src/Bundle/ChillMainBundle/Resources/views/layoutWithVerticalMenu.html.twig b/src/Bundle/ChillMainBundle/Resources/views/layoutWithVerticalMenu.html.twig index 21a56b027..cfbc16f4f 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/layoutWithVerticalMenu.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/layoutWithVerticalMenu.html.twig @@ -29,7 +29,7 @@
{# Flash messages ! #} - {% if app.session.flashbag.all()|length > 0 %} + {% if app.session.flashbag.keys()|length > 0 %}
{% for flashMessage in app.session.flashbag.get('success') %} diff --git a/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml b/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml new file mode 100644 index 000000000..d5dedbb9f --- /dev/null +++ b/src/Bundle/ChillMainBundle/translations/messages+intl-icu.fr.yaml @@ -0,0 +1,6 @@ +years_old: >- + {age, plural, + one {# an} + many {# ans} + other {# ans} + } diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index c4b36a348..7f9805f52 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -734,9 +734,13 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $this->birthdate; } - public function getAge(): int + public function getAge(): ?int { - return date_diff($this->birthdate, date_create('now'))->format("%y"); + if ($this->birthdate instanceof \DateTimeInterface) { + return date_diff($this->birthdate, date_create('now'))->format("%y"); + } + + return null; } /** diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Person/banner.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Person/banner.html.twig index dd136014e..6fb12847d 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Person/banner.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Person/banner.html.twig @@ -76,10 +76,10 @@ {{ 'Unknown date of birth'|trans }} {% else %} {{ person.birthdate|format_date('short') }} + + {{ 'years_old'|trans({ 'age': person.age }) }} + {% endif %} - - {{ person.age ~ ((person.age > 1) ? ' ans' : ' an') }} -
{%- if chill_person.fields.nationality == 'visible' -%} diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingPeriodControllerTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingPeriodControllerTest.php index 425b00b6c..e841f2615 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingPeriodControllerTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingPeriodControllerTest.php @@ -152,7 +152,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase * with : dateClosing: 2015-02-01 * with : the last closing motive in list * Then the response should redirect to period view - * And the next page should have a `.error` element present in page + * And the next page should have a `.alert-danger` element present in page * * @todo */ @@ -174,7 +174,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase '/fr/person/'.$this->person->getId().'/accompanying-period'), 'the server redirects to /accompanying-period page'); $this->assertGreaterThan(0, $this->client->followRedirect() - ->filter('.success')->count(), + ->filter('.alert-success')->count(), "a 'success' element is shown"); } @@ -186,7 +186,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase * with : dateClosing: 2014-01-01 * with : the last closing motive in list * Then the response should redirect to period view - * And the next page should have a `.error` element present in page + * And the next page should have a `.alert-danger` element present in page * * @todo */ @@ -207,8 +207,8 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stays on the /close page'); $this->assertGreaterThan(0, $crawlerResponse - ->filter('.error')->count(), - "an '.error' element is shown"); + ->filter('.alert-danger')->count(), + "an '.alert-danger' element is shown"); } /** @@ -240,7 +240,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase '/fr/person/'.$this->person->getId().'/accompanying-period'), 'the server redirects to /accompanying-period page'); $this->assertGreaterThan(0, $this->client->followRedirect() - ->filter('.success')->count(), + ->filter('.alert-success')->count(), "a 'success' element is shown"); } @@ -275,7 +275,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stay on form page'); - $this->assertGreaterThan(0, $crawler->filter('.error')->count(), + $this->assertGreaterThan(0, $crawler->filter('.alert-danger')->count(), "an 'error' element is shown"); } @@ -310,7 +310,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stay on form page'); - $this->assertGreaterThan(0, $crawler->filter('.error')->count(), + $this->assertGreaterThan(0, $crawler->filter('.alert-danger')->count(), "an 'error' element is shown"); } @@ -351,7 +351,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stay on form page'); - $this->assertGreaterThan(0, $crawlerResponse->filter('.error')->count(), + $this->assertGreaterThan(0, $crawlerResponse->filter('.alert-danger')->count(), "an 'error' element is shown"); } @@ -382,7 +382,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stay on form page'); - $this->assertGreaterThan(0, $crawler->filter('.error')->count(), + $this->assertGreaterThan(0, $crawler->filter('.alert-danger')->count(), "an 'error' element is shown"); } @@ -424,7 +424,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stay on form page'); - $this->assertGreaterThan(0, $crawlerResponse->filter('.error')->count(), + $this->assertGreaterThan(0, $crawlerResponse->filter('.alert-danger')->count(), "an 'error' element is shown"); } @@ -465,7 +465,7 @@ class AccompanyingPeriodControllerTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the server stay on form page'); - $this->assertGreaterThan(0, $crawlerResponse->filter('.error')->count(), + $this->assertGreaterThan(0, $crawlerResponse->filter('.alert-danger')->count(), "an 'error' element is shown"); } diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php index ffdd65f35..8a8c04538 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerUpdateTest.php @@ -102,7 +102,6 @@ class PersonControllerUpdateTest extends WebTestCase public function testHiddenFielsArePresent() { $crawler = $this->client->request('GET', $this->editUrl); - $configurables = array('placeOfBirth', 'phonenumber', 'email', 'countryOfBirth', 'nationality', 'spokenLanguages', 'maritalStatus'); $form = $crawler->selectButton('Enregistrer')->form(); //; @@ -190,7 +189,7 @@ class PersonControllerUpdateTest extends WebTestCase 'the value '.$field.' is updated in db'); $crawler = $this->client->followRedirect(); - $this->assertGreaterThan(0, $crawler->filter('.success')->count(), + $this->assertGreaterThan(0, $crawler->filter('.alert-success')->count(), 'a element .success is shown'); if($field == 'birthdate' or $field == 'memo' or $field == 'countryOfBirth' or $field == 'nationality' @@ -245,7 +244,7 @@ class PersonControllerUpdateTest extends WebTestCase $this->assertFalse($this->client->getResponse()->isRedirect(), 'the page is not redirected to /general'); - $this->assertGreaterThan(0, $crawler->filter('.error')->count(), + $this->assertGreaterThan(0, $crawler->filter('.alert-danger')->count(), 'a element .error is shown'); } From 18ab10dd45cd0250f83377133fff8b4d4841669c Mon Sep 17 00:00:00 2001 From: Marc Ducobu Date: Fri, 18 Jun 2021 17:18:07 +0200 Subject: [PATCH 13/24] Add center a_social to an activityNotif feature --- .../DataFixtures/ORM/LoadActivityNotifications.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivityNotifications.php b/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivityNotifications.php index 7835a0db4..9ef67a7ac 100644 --- a/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivityNotifications.php +++ b/src/Bundle/ChillActivityBundle/DataFixtures/ORM/LoadActivityNotifications.php @@ -22,6 +22,7 @@ class LoadActivityNotifications extends AbstractFixture implements DependentFixt 'entityRef' => 'activity_gerard depardieu', 'sender' => 'center a_social', 'addressees' => [ + 'center a_social', 'center a_administrative', 'center a_direction', 'multi_center' From bdf691a063c43f8acdcc16d2469be9404b8bf648 Mon Sep 17 00:00:00 2001 From: Marc Ducobu Date: Fri, 18 Jun 2021 17:19:39 +0200 Subject: [PATCH 14/24] Add findAllForAttendee method in NotificationRepository --- .../Repository/NotificationRepository.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php index 844459f8b..d52817164 100644 --- a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php @@ -23,6 +23,7 @@ use Chill\MainBundle\Entity\Notification; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ObjectRepository; +use Chill\MainBundle\Entity\User; final class NotificationRepository implements ObjectRepository { @@ -59,8 +60,24 @@ final class NotificationRepository implements ObjectRepository return $this->repository->findBy($criteria, $orderBy, $limit, $offset); } + /** + * @return Notification[] + */ + public function findAllForAttendee(User $addressee) // TODO passer à attendees avec S + { + $qb = $this->repository->createQueryBuilder('n'); + + $qb + ->select('n') + ->join('n.addressees', 'a') + ->where('a = :addressee') + ->setParameter('addressee', $addressee); + + $query = $qb->getQuery(); + return $qb->getQuery()->getResult(); + } + public function getClassName() { return Notification::class; } - } From 282db51f06b6136c227fb7297f38c89c2880a6a6 Mon Sep 17 00:00:00 2001 From: Marc Ducobu Date: Fri, 18 Jun 2021 18:05:02 +0200 Subject: [PATCH 15/24] Display notification using services (draft) --- .../ChillActivityExtension.php | 1 + .../ActivityNotificationRenderer.php | 24 ++++++++++ .../Activity/showInNotification.html.twig | 4 ++ .../config/services/notification.yaml | 3 ++ .../Controller/NotificationController.php | 42 ++++++++++++++++++ .../Notification/NotificationRenderer.php | 44 +++++++++++++++++++ .../views/Notification/show.html.twig | 42 ++++++++++++++++++ src/Bundle/ChillMainBundle/config/routes.yaml | 4 ++ .../config/routes/notification.yaml | 3 ++ .../config/services/controller.yaml | 5 +++ .../config/services/notification.yaml | 6 ++- .../ChillPersonExtension.php | 1 + ...AccompanyingPeriodNotificationRenderer.php | 24 ++++++++++ .../showInNotification.html.twig | 3 ++ .../config/services/notification.yaml | 3 ++ 15 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php create mode 100644 src/Bundle/ChillActivityBundle/Resources/views/Activity/showInNotification.html.twig create mode 100644 src/Bundle/ChillActivityBundle/config/services/notification.yaml create mode 100644 src/Bundle/ChillMainBundle/Controller/NotificationController.php create mode 100644 src/Bundle/ChillMainBundle/Notification/NotificationRenderer.php create mode 100644 src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig create mode 100644 src/Bundle/ChillMainBundle/config/routes/notification.yaml create mode 100644 src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationRenderer.php create mode 100644 src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/showInNotification.html.twig create mode 100644 src/Bundle/ChillPersonBundle/config/services/notification.yaml diff --git a/src/Bundle/ChillActivityBundle/DependencyInjection/ChillActivityExtension.php b/src/Bundle/ChillActivityBundle/DependencyInjection/ChillActivityExtension.php index 61354c7c8..e240bf17c 100644 --- a/src/Bundle/ChillActivityBundle/DependencyInjection/ChillActivityExtension.php +++ b/src/Bundle/ChillActivityBundle/DependencyInjection/ChillActivityExtension.php @@ -55,6 +55,7 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf $loader->load('services/controller.yaml'); $loader->load('services/form.yaml'); $loader->load('services/templating.yaml'); + $loader->load('services/notification.yaml'); } public function prepend(ContainerBuilder $container) diff --git a/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php b/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php new file mode 100644 index 000000000..f1134d421 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php @@ -0,0 +1,24 @@ +getRelatedEntityClass() == Activity::class; + } + + public function getTemplate() + { + return 'ChillActivityBundle:Activity:showInNotification.html.twig'; + } + + public function getTemplateData(Notification $notification) + { + return ['notification' => $notification]; + } +} diff --git a/src/Bundle/ChillActivityBundle/Resources/views/Activity/showInNotification.html.twig b/src/Bundle/ChillActivityBundle/Resources/views/Activity/showInNotification.html.twig new file mode 100644 index 000000000..5128e9a64 --- /dev/null +++ b/src/Bundle/ChillActivityBundle/Resources/views/Activity/showInNotification.html.twig @@ -0,0 +1,4 @@ + +{{ dump(notification) }} + +Go to Activity diff --git a/src/Bundle/ChillActivityBundle/config/services/notification.yaml b/src/Bundle/ChillActivityBundle/config/services/notification.yaml new file mode 100644 index 000000000..e3667550c --- /dev/null +++ b/src/Bundle/ChillActivityBundle/config/services/notification.yaml @@ -0,0 +1,3 @@ +services: + Chill\ActivityBundle\Notification\ActivityNotificationRenderer: + autowire: true diff --git a/src/Bundle/ChillMainBundle/Controller/NotificationController.php b/src/Bundle/ChillMainBundle/Controller/NotificationController.php new file mode 100644 index 000000000..83b77d7b6 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/NotificationController.php @@ -0,0 +1,42 @@ +security = $security; + } + + public function showAction(NotificationRepository $notificationRepository, NotificationRenderer $notificationRenderer) + { + $currentUser = $this->security->getUser(); + + $notifications = $notificationRepository->findAllForAttendee($currentUser); + + $templateData = array(); + foreach ($notifications as $notification) { + $data = [ + 'template' => $notificationRenderer->getTemplate($notification), + 'template_data' => $notificationRenderer->getTemplateData($notification), + 'notification' => $notification + ]; + $templateData[] = $data; + } + + return $this->render('ChillMainBundle:Notification:show.html.twig', [ + 'datas' => $templateData, + 'notifications' => $notifications, + ]); + } +} diff --git a/src/Bundle/ChillMainBundle/Notification/NotificationRenderer.php b/src/Bundle/ChillMainBundle/Notification/NotificationRenderer.php new file mode 100644 index 000000000..d6f383b73 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Notification/NotificationRenderer.php @@ -0,0 +1,44 @@ +renderers[] = $accompanyingPeriodNotificationRenderer; + $this->renderers[] = $activityNotificationRenderer; + } + + private function getRenderer(Notification $notification) + { + foreach ($this->renderers as $renderer) { + if($renderer->supports($notification)) { + return $renderer; + } + } + + throw new \Exception('No renderer for '. $notification); + } + + public function getTemplate(Notification $notification) + { + return $this->getRenderer($notification)->getTemplate(); + } + + public function getTemplateData(Notification $notification) + { + return $this->getRenderer($notification)->getTemplateData($notification); + } +} diff --git a/src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig new file mode 100644 index 000000000..34aeee049 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Notification/show.html.twig @@ -0,0 +1,42 @@ +{% extends "@ChillMain/layout.html.twig" %} + +{% block content %} +
+
+

{{ "Notifications list" | trans }}

+ + + {%for data in datas %} + {% set notification = data.notification %} + +
+
{{ 'Message'|trans }}
+
{{ notification.message }}
+
+ +
+
{{ 'Date'|trans }}
+
{{ notification.date | date('long') }}
+
+ + +
+
{{ 'Sender'|trans }}
+
{{ notification.sender }}
+
+ +
+
{{ 'Addressees'|trans }}
+
{{ notification.addressees |join(', ') }}
+
+ +
+
{{ 'Entity'|trans }}
+
+ {% include data.template with data.template_data %} +
+
+ {% endfor %} +
+
+{% endblock content %} diff --git a/src/Bundle/ChillMainBundle/config/routes.yaml b/src/Bundle/ChillMainBundle/config/routes.yaml index 240a44590..697ec8ec0 100644 --- a/src/Bundle/ChillMainBundle/config/routes.yaml +++ b/src/Bundle/ChillMainBundle/config/routes.yaml @@ -34,6 +34,10 @@ chill_password_recover: resource: "@ChillMainBundle/config/routes/password_recover.yaml" prefix: "public/{_locale}/password" +chill_main_notification: + resource: "@ChillMainBundle/config/routes/notification.yaml" + prefix: "{_locale}/notification" + chill_crud: resource: "@ChillMainBundle" type: CRUD diff --git a/src/Bundle/ChillMainBundle/config/routes/notification.yaml b/src/Bundle/ChillMainBundle/config/routes/notification.yaml new file mode 100644 index 000000000..2112f882f --- /dev/null +++ b/src/Bundle/ChillMainBundle/config/routes/notification.yaml @@ -0,0 +1,3 @@ +chill_main_notification_show: + path: /show + controller: Chill\MainBundle\Controller\NotificationController::showAction diff --git a/src/Bundle/ChillMainBundle/config/services/controller.yaml b/src/Bundle/ChillMainBundle/config/services/controller.yaml index 6021e3d72..11a9bc274 100644 --- a/src/Bundle/ChillMainBundle/config/services/controller.yaml +++ b/src/Bundle/ChillMainBundle/config/services/controller.yaml @@ -33,3 +33,8 @@ services: $logger: '@Psr\Log\LoggerInterface' $validator: '@Symfony\Component\Validator\Validator\ValidatorInterface' tags: ['controller.service_arguments'] + + Chill\MainBundle\Controller\NotificationController: + arguments: + $security: '@Symfony\Component\Security\Core\Security' + tags: ['controller.service_arguments'] diff --git a/src/Bundle/ChillMainBundle/config/services/notification.yaml b/src/Bundle/ChillMainBundle/config/services/notification.yaml index fa4fa15c6..c8d970c5d 100644 --- a/src/Bundle/ChillMainBundle/config/services/notification.yaml +++ b/src/Bundle/ChillMainBundle/config/services/notification.yaml @@ -4,7 +4,11 @@ services: $logger: '@Psr\Log\LoggerInterface' $twig: '@Twig\Environment' $mailer: '@swiftmailer.mailer.default' - # $mailerTransporter: '@swiftmailer.transport' + # $mailerTransporter: '@swiftmailer.transport' $router: '@Symfony\Component\Routing\RouterInterface' $translator: '@Symfony\Component\Translation\TranslatorInterface' $routeParameters: '%chill_main.notifications%' + + Chill\MainBundle\Notification\NotificationRenderer: + autoconfigure: true + autowire: true diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php index 03b05bbc8..1b22018a2 100644 --- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php +++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php @@ -74,6 +74,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac $loader->load('services/form.yaml'); $loader->load('services/alt_names.yaml'); $loader->load('services/household.yaml'); + $loader->load('services/notification.yaml'); // We can get rid of this file when the service 'chill.person.repository.person' is no more used. // We should use the PersonRepository service instead of a custom service name. $loader->load('services/repository.yaml'); diff --git a/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationRenderer.php b/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationRenderer.php new file mode 100644 index 000000000..ca76fbd5c --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Notification/AccompanyingPeriodNotificationRenderer.php @@ -0,0 +1,24 @@ +getRelatedEntityClass() == AccompanyingPeriod::class; + } + + public function getTemplate() + { + return 'ChillPersonBundle:AccompanyingPeriod:showInNotification.html.twig'; + } + + public function getTemplateData(Notification $notification) + { + return ['notification' => $notification]; + } +} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/showInNotification.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/showInNotification.html.twig new file mode 100644 index 000000000..d92de8cba --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/showInNotification.html.twig @@ -0,0 +1,3 @@ + + Go to Acc. period. + diff --git a/src/Bundle/ChillPersonBundle/config/services/notification.yaml b/src/Bundle/ChillPersonBundle/config/services/notification.yaml new file mode 100644 index 000000000..58187defb --- /dev/null +++ b/src/Bundle/ChillPersonBundle/config/services/notification.yaml @@ -0,0 +1,3 @@ +services: + Chill\PersonBundle\Notification\AccompanyingPeriodNotificationRenderer: + autowire: true From 673a4761e9e873e2af57c21452f20cae8f86220a Mon Sep 17 00:00:00 2001 From: Marc Ducobu Date: Fri, 18 Jun 2021 18:05:27 +0200 Subject: [PATCH 16/24] Adding feature for AccompanyingPeriodNotif --- .../LoadAccompanyingPeriodNotifications.php | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriodNotifications.php diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriodNotifications.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriodNotifications.php new file mode 100644 index 000000000..d1c6f4da7 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriodNotifications.php @@ -0,0 +1,39 @@ + 'Hello !', + 'entityClass' => AccompanyingPeriod::class, + 'entityRef' => LoadAccompanyingPeriod::ACCOMPANYING_PERIOD, + 'sender' => 'center a_social', + 'addressees' => [ + 'center a_social', + 'center a_administrative', + 'center a_direction', + 'multi_center' + ], + ] + ]; + + public function getDependencies() + { + return [ + LoadAccompanyingPeriod::class, + ]; + } +} From 0907ae16020dfd1a97bdd493cfded327fcec832d Mon Sep 17 00:00:00 2001 From: Marc Ducobu Date: Wed, 30 Jun 2021 11:33:19 +0200 Subject: [PATCH 17/24] Rmq Julien & Pol --- .../Notification/ActivityNotificationRenderer.php | 4 ++-- .../ChillActivityBundle/config/services/notification.yaml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php b/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php index f1134d421..1836f599b 100644 --- a/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php +++ b/src/Bundle/ChillActivityBundle/Notification/ActivityNotificationRenderer.php @@ -7,14 +7,14 @@ use Chill\ActivityBundle\Entity\Activity; final class ActivityNotificationRenderer { - public function supports(Notification $notification) + public function supports(Notification $notification, array $options = []): bool { return $notification->getRelatedEntityClass() == Activity::class; } public function getTemplate() { - return 'ChillActivityBundle:Activity:showInNotification.html.twig'; + return '@ChillActivity/Activity/showInNotification.html.twig'; } public function getTemplateData(Notification $notification) diff --git a/src/Bundle/ChillActivityBundle/config/services/notification.yaml b/src/Bundle/ChillActivityBundle/config/services/notification.yaml index e3667550c..3db168ecd 100644 --- a/src/Bundle/ChillActivityBundle/config/services/notification.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/notification.yaml @@ -1,3 +1,4 @@ services: Chill\ActivityBundle\Notification\ActivityNotificationRenderer: + autoconfigure: true autowire: true From 50a4df3d0f7a1e7aa8d89bf4f763b057c62ea73d Mon Sep 17 00:00:00 2001 From: Marc Ducobu Date: Wed, 30 Jun 2021 12:02:18 +0200 Subject: [PATCH 18/24] Notification with paginitation --- .../Controller/NotificationController.php | 29 +++++++++--- .../Repository/NotificationRepository.php | 45 ++++++++++++++++--- .../config/routes/notification.yaml | 3 -- .../config/services/pagination.yaml | 4 +- 4 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/NotificationController.php b/src/Bundle/ChillMainBundle/Controller/NotificationController.php index 83b77d7b6..c76e19d9e 100644 --- a/src/Bundle/ChillMainBundle/Controller/NotificationController.php +++ b/src/Bundle/ChillMainBundle/Controller/NotificationController.php @@ -3,12 +3,16 @@ namespace Chill\MainBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Chill\MainBundle\Entity\User; use Chill\MainBundle\Repository\NotificationRepository; use Chill\MainBundle\Notification\NotificationRenderer; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Security; +use Symfony\Component\Routing\Annotation\Route; +use Chill\MainBundle\Pagination\PaginatorFactory; + +/** + * @Route("/{_locale}/notification") + */ class NotificationController extends AbstractController { private $security; @@ -18,11 +22,23 @@ class NotificationController extends AbstractController $this->security = $security; } - public function showAction(NotificationRepository $notificationRepository, NotificationRenderer $notificationRenderer) + + /** + * @Route("/show", name="chill_main_notification_show") + */ + public function showAction( + NotificationRepository $notificationRepository, NotificationRenderer $notificationRenderer, + PaginatorFactory $paginatorFactory) { $currentUser = $this->security->getUser(); - $notifications = $notificationRepository->findAllForAttendee($currentUser); + $notificationsNbr = $notificationRepository->countAllForAttendee(($currentUser)); + $paginator = $paginatorFactory->create($notificationsNbr); + + $notifications = $notificationRepository->findAllForAttendee( + $currentUser, + $limit=$paginator->getItemsPerPage(), + $offset= $paginator->getCurrentPage()->getFirstItemNumber()); $templateData = array(); foreach ($notifications as $notification) { @@ -34,9 +50,10 @@ class NotificationController extends AbstractController $templateData[] = $data; } - return $this->render('ChillMainBundle:Notification:show.html.twig', [ + return $this->render('@ChillMain/Notification/show.html.twig', [ 'datas' => $templateData, - 'notifications' => $notifications, + 'notifications' => $notifications, + 'paginator' => $paginator, ]); } } diff --git a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php index d52817164..5ffda75dc 100644 --- a/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/NotificationRepository.php @@ -24,6 +24,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ObjectRepository; use Chill\MainBundle\Entity\User; +use Doctrine\ORM\Query; final class NotificationRepository implements ObjectRepository { @@ -60,21 +61,51 @@ final class NotificationRepository implements ObjectRepository return $this->repository->findBy($criteria, $orderBy, $limit, $offset); } - /** - * @return Notification[] - */ - public function findAllForAttendee(User $addressee) // TODO passer à attendees avec S + private function queryAllForAttendee(User $addressee, bool $countQuery=False): Query { $qb = $this->repository->createQueryBuilder('n'); + $select = 'n'; + if($countQuery) { + $select = 'count(n)'; + } + $qb - ->select('n') + ->select($select) ->join('n.addressees', 'a') ->where('a = :addressee') ->setParameter('addressee', $addressee); - $query = $qb->getQuery(); - return $qb->getQuery()->getResult(); + return $qb->getQuery(); + } + + /** + * @return int + */ + public function countAllForAttendee(User $addressee): int // TODO passer à attendees avec S + { + $query = $this->queryAllForAttendee($addressee, $countQuery=True); + + return $query->getSingleScalarResult(); + } + + + /** + * @return Notification[] + */ + public function findAllForAttendee(User $addressee, $limit = null, $offset = null): array // TODO passer à attendees avec S + { + $query = $this->queryAllForAttendee($addressee); + + if($limit) { + $query = $query->setMaxResults($limit); + } + + if($offset) { + $query = $query->setFirstResult($offset); + } + + return $query->getResult(); } public function getClassName() { diff --git a/src/Bundle/ChillMainBundle/config/routes/notification.yaml b/src/Bundle/ChillMainBundle/config/routes/notification.yaml index 2112f882f..e69de29bb 100644 --- a/src/Bundle/ChillMainBundle/config/routes/notification.yaml +++ b/src/Bundle/ChillMainBundle/config/routes/notification.yaml @@ -1,3 +0,0 @@ -chill_main_notification_show: - path: /show - controller: Chill\MainBundle\Controller\NotificationController::showAction diff --git a/src/Bundle/ChillMainBundle/config/services/pagination.yaml b/src/Bundle/ChillMainBundle/config/services/pagination.yaml index f6282a39f..e85352728 100644 --- a/src/Bundle/ChillMainBundle/config/services/pagination.yaml +++ b/src/Bundle/ChillMainBundle/config/services/pagination.yaml @@ -2,13 +2,15 @@ services: chill_main.paginator_factory: class: Chill\MainBundle\Pagination\PaginatorFactory public: true + autowire: true + autoconfigure: true arguments: - "@request_stack" - "@router" - "%chill_main.pagination.item_per_page%" Chill\MainBundle\Pagination\PaginatorFactory: '@chill_main.paginator_factory' - + chill_main.paginator.twig_extensions: class: Chill\MainBundle\Pagination\ChillPaginationTwig tags: From 8030792eb65300aa37e892a636d0ba3d606735ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 22 Jul 2021 15:36:36 +0000 Subject: [PATCH 19/24] Remove creation of AccompanyingPeriod on person creation --- .../Controller/PersonController.php | 13 ----- .../DataFixtures/ORM/LoadPeople.php | 8 +++ .../ChillPersonBundle/Entity/Person.php | 13 +---- .../Form/CreationPersonType.php | 18 ++---- .../Resources/views/Person/create.html.twig | 5 +- .../views/Person/create_review.html.twig | 16 ++++-- .../Controller/PersonControllerCreateTest.php | 6 -- .../Tests/Entity/PersonTest.php | 17 ++++-- .../TimelineAccompanyingPeriodTest.php | 57 ++++++++++++++----- 9 files changed, 78 insertions(+), 75 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Controller/PersonController.php b/src/Bundle/ChillPersonBundle/Controller/PersonController.php index 747299e59..f33e9cce8 100644 --- a/src/Bundle/ChillPersonBundle/Controller/PersonController.php +++ b/src/Bundle/ChillPersonBundle/Controller/PersonController.php @@ -252,18 +252,6 @@ final class PersonController extends AbstractController */ $person = $form->getData(); - $periods = $person->getAccompanyingPeriodsOrdered(); - $period = $periods[0]; - $period->setOpeningDate($form['creation_date']->getData()); -// $person = new Person($form['creation_date']->getData()); -// -// $person->setFirstName($form['firstName']->getData()) -// ->setLastName($form['lastName']->getData()) -// ->setGender($form['gender']->getData()) -// ->setBirthdate($form['birthdate']->getData()) -// ->setCenter($form['center']->getData()) -// ; - return $person; } @@ -364,7 +352,6 @@ final class PersonController extends AbstractController 'lastName' => $form['lastName']->getData(), 'birthdate' => $form['birthdate']->getData(), 'gender' => $form['gender']->getData(), - 'creation_date' => $form['creation_date']->getData(), 'form' => $form->createView())); } diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPeople.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPeople.php index e8697ff28..556d832c1 100644 --- a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPeople.php +++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadPeople.php @@ -183,6 +183,14 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con private function addAPerson(array $person, ObjectManager $manager) { $p = new Person(); + $p->addAccompanyingPeriod( + new AccompanyingPeriod( + (new \DateTime()) + ->sub( + new \DateInterval('P'.\random_int(0, 180).'D') + ) + ) + ); foreach ($person as $key => $value) { switch ($key) { diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 7f9805f52..695970bfa 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -379,12 +379,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI */ private Collection $householdAddresses; - /** - * Person constructor. - * - * @param \DateTime|null $opening - */ - public function __construct(\DateTime $opening = null) + public function __construct() { $this->accompanyingPeriodParticipations = new ArrayCollection(); $this->spokenLanguages = new ArrayCollection(); @@ -393,12 +388,6 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI $this->otherPhoneNumbers = new ArrayCollection(); $this->householdParticipations = new ArrayCollection(); $this->householdAddresses = new ArrayCollection(); - - if ($opening === null) { - $opening = new \DateTime(); - } - - $this->open(new AccompanyingPeriod($opening)); $this->genderComment = new CommentEmbeddable(); $this->maritalStatusComment = new CommentEmbeddable(); } diff --git a/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php b/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php index e3583071f..c001185ca 100644 --- a/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php +++ b/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php @@ -26,7 +26,7 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; use Symfony\Component\Form\Extension\Core\Type\HiddenType; -use Symfony\Component\Form\Extension\Core\Type\DateType; +use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\CenterType; use Chill\PersonBundle\Form\Type\GenderType; use Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer; @@ -80,9 +80,6 @@ final class CreationPersonType extends AbstractType 'property_path' => 'birthdate' )) ->add('gender', HiddenType::class) - ->add('creation_date', HiddenType::class, array( - 'mapped' => false - )) ->add('form_status', HiddenType::class, array( 'mapped' => false, 'data' => $options['form_status'] @@ -99,25 +96,18 @@ final class CreationPersonType extends AbstractType $builder->get('birthdate') ->addModelTransformer($dateToStringTransformer); - $builder->get('creation_date') - ->addModelTransformer($dateToStringTransformer); $builder->get('center') ->addModelTransformer($this->centerTransformer); } else { $builder ->add('firstName') ->add('lastName') - ->add('birthdate', DateType::class, array('required' => false, - 'widget' => 'single_text', 'format' => 'dd-MM-yyyy')) + ->add('birthdate', ChillDateType::class, [ + 'required' => false, + ]) ->add('gender', GenderType::class, array( 'required' => true, 'placeholder' => null )) - ->add('creation_date', DateType::class, array( - 'required' => true, - 'widget' => 'single_text', - 'format' => 'dd-MM-yyyy', - 'mapped' => false, - 'data' => new \DateTime())) ->add('form_status', HiddenType::class, array( 'data' => $options['form_status'], 'mapped' => false diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Person/create.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Person/create.html.twig index 9d17cba9d..e7a6fb4a6 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Person/create.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Person/create.html.twig @@ -1,7 +1,6 @@ {# * Copyright (C) 2014, Champs Libres Cooperative SCRLFS, - * - * This program is free software: you can redistribute it and/or modify + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. @@ -38,8 +37,6 @@ {{ form_row(form.birthdate, { 'label' : 'Date of birth'|trans }) }} {{ form_row(form.gender, { 'label' : 'Gender'|trans }) }} - - {{ form_row(form.creation_date, { 'label' : 'Creation date'|trans }) }} {{ form_rest(form) }} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Person/create_review.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Person/create_review.html.twig index 8c5b6eb35..cd32c180c 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Person/create_review.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Person/create_review.html.twig @@ -71,14 +71,15 @@
{{ person|chill_entity_render_string }}
{{ 'Date of birth'|trans }}
-
{{ birthdate|format_date('long')|default( 'Unknown date of birth'|trans ) }}
+ {% if birthdate is empty %} +
{{ 'Unknown date of birth'|trans }}
+ {% else %} +
{{ birthdate|format_date('long') }}
+ {% endif %}
{{ 'Gender'|trans }}
{{ gender|trans }}
-
{{ 'Creation date'|trans }}
-
{{ creation_date|format_date('long') }}
- {% if form.altNames is defined %} {# mark as rendered #} {{ form_widget(form.altNames) }} @@ -86,10 +87,13 @@ {{ form_rest(form) }} - +
    +
  • + +
  • +
{{ form_end(form) }}
-
diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerCreateTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerCreateTest.php index 57444dfaa..f7dd055c6 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerCreateTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonControllerCreateTest.php @@ -93,8 +93,6 @@ class PersonControllerCreateTest extends WebTestCase 'The page contains a "gender" input'); $this->assertTrue($form->has(self::BIRTHDATE_INPUT), 'The page has a "date of birth" input'); - $this->assertTrue($form->has(self::CREATEDATE_INPUT), - 'The page contains a "creation date" input'); $genderType = $form->get(self::GENDER_INPUT); $this->assertEquals('radio', $genderType->getType(), @@ -107,10 +105,6 @@ class PersonControllerCreateTest extends WebTestCase 'gender has "femme" option'); $this->assertFalse($genderType->hasValue(), 'The gender input is not checked'); - $today = new \DateTime(); - $this->assertEquals($today->format('d-m-Y'), $form->get(self::CREATEDATE_INPUT) - ->getValue(), 'The creation date input has the current date by default'); - return $form; } diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php index 7e9337d14..dbef739d1 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/PersonTest.php @@ -45,11 +45,12 @@ class PersonTest extends \PHPUnit\Framework\TestCase public function testGetCurrentAccompanyingPeriod() { $d = new \DateTime('yesterday'); - $p = new Person($d); + $p = new Person(); + $p->addAccompanyingPeriod(new AccompanyingPeriod($d)); $period = $p->getCurrentAccompanyingPeriod(); - $this->assertInstanceOf('Chill\PersonBundle\Entity\AccompanyingPeriod', $period); + $this->assertInstanceOf(AccompanyingPeriod::class, $period); $this->assertTrue($period->isOpen()); $this->assertEquals($d, $period->getOpeningDate()); @@ -67,7 +68,8 @@ class PersonTest extends \PHPUnit\Framework\TestCase public function testAccompanyingPeriodOrderWithUnorderedAccompanyingPeriod() { $d = new \DateTime("2013/2/1"); - $p = new Person($d); + $p = new Person(); + $p->addAccompanyingPeriod(new AccompanyingPeriod($d)); $e = new \DateTime("2013/3/1"); $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e); @@ -93,7 +95,8 @@ class PersonTest extends \PHPUnit\Framework\TestCase */ public function testAccompanyingPeriodOrderSameDateOpening() { $d = new \DateTime("2013/2/1"); - $p = new Person($d); + $p = new Person(); + $p->addAccompanyingPeriod(new AccompanyingPeriod($d)); $g = new \DateTime("2013/4/1"); $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($g); @@ -120,7 +123,8 @@ class PersonTest extends \PHPUnit\Framework\TestCase */ public function testDateCoveringWithCoveringAccompanyingPeriod() { $d = new \DateTime("2013/2/1"); - $p = new Person($d); + $p = new Person(); + $p->addAccompanyingPeriod(new AccompanyingPeriod($d)); $e = new \DateTime("2013/3/1"); $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e); @@ -145,7 +149,8 @@ class PersonTest extends \PHPUnit\Framework\TestCase */ public function testNotOpenAFileReOpenedLater() { $d = new \DateTime("2013/2/1"); - $p = new Person($d); + $p = new Person(); + $p->addAccompanyingPeriod(new AccompanyingPeriod($d)); $e = new \DateTime("2013/3/1"); $period = $p->getCurrentAccompanyingPeriod()->setClosingDate($e); diff --git a/src/Bundle/ChillPersonBundle/Tests/Timeline/TimelineAccompanyingPeriodTest.php b/src/Bundle/ChillPersonBundle/Tests/Timeline/TimelineAccompanyingPeriodTest.php index 4fb3c7f06..9b2a32c85 100644 --- a/src/Bundle/ChillPersonBundle/Tests/Timeline/TimelineAccompanyingPeriodTest.php +++ b/src/Bundle/ChillPersonBundle/Tests/Timeline/TimelineAccompanyingPeriodTest.php @@ -20,9 +20,11 @@ namespace Chill\PersonBundle\Tests\Timeline; -use Symfony\Bundle\SecurityBundle\Tests\Functional\WebTestCase; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Doctrine\ORM\EntityManagerInterface; +use Chill\MainBundle\Test\PrepareClientTrait; /** * This class tests entries are shown for closing and opening @@ -31,22 +33,20 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod; * @author Julien Fastré * @author Champs Libres */ -class TimelineAccompanyingPeriodTest extends \Chill\PersonBundle\Tests\Controller\AccompanyingPeriodControllerTest +class TimelineAccompanyingPeriodTest extends WebTestCase { - public function testEntriesAreShown() + use PrepareClientTrait; + + /** + * @dataProvider provideDataPersonWithAccompanyingPeriod + */ + public function testEntriesAreShown($personId) { - $this->generatePeriods(array( - [ - 'openingDate' => '2014-01-01', - 'closingDate' => '2014-12-31', - 'closingMotive' => $this->getRandomClosingMotive() - ] - )); + $client = $this->getClientAuthenticated(); + + $crawler = $client->request('GET', "/en/person/{$personId}/timeline"); - $crawler = $this->client->request('GET', '/en/person/' - .$this->person->getId().'/timeline'); - - $this->assertTrue($this->client->getResponse()->isSuccessful(), + $this->assertTrue($client->getResponse()->isSuccessful(), "the timeline page loads sucessfully"); $this->assertGreaterThan(0, $crawler->filter('.timeline div')->count(), "the timeline page contains multiple div inside a .timeline element"); @@ -57,5 +57,34 @@ class TimelineAccompanyingPeriodTest extends \Chill\PersonBundle\Tests\Controlle $crawler->Filter('.timeline')->text(), "the text 'Une période d'accompagnement a été fermée' is present"); } + + public function provideDataPersonWithAccompanyingPeriod() + { + self::bootKernel(); + + $qb = self::$container->get(EntityManagerInterface::class) + ->createQueryBuilder() + ; + $personIds = $qb + ->from(Person::class, 'p') + ->join('p.accompanyingPeriodParticipations', 'part') + ->join('part.accompanyingPeriod', 'period') + ->join('p.center', 'center') + ->select('p.id') + ->where($qb->expr()->isNotNull('period.closingDate')) + ->andWhere($qb->expr()->eq('center.name', ':center')) + ->setParameter('center', 'Center A') + ->setMaxResults(1000) + ->getQuery() + ->getResult() + ; + + \shuffle($personIds); + + yield [ \array_pop($personIds)['id'] ]; + yield [ \array_pop($personIds)['id'] ]; + yield [ \array_pop($personIds)['id'] ]; + + } } From 0fd9485de5e0a14c717723356099b22b20237a35 Mon Sep 17 00:00:00 2001 From: Marc Ducobu Date: Thu, 22 Jul 2021 18:15:57 +0200 Subject: [PATCH 20/24] Avoid creation of services/notification.yaml --- .../DependencyInjection/ChillActivityExtension.php | 1 - src/Bundle/ChillActivityBundle/config/services.yaml | 13 +++++++++---- .../config/services/notification.yaml | 4 ---- 3 files changed, 9 insertions(+), 9 deletions(-) delete mode 100644 src/Bundle/ChillActivityBundle/config/services/notification.yaml diff --git a/src/Bundle/ChillActivityBundle/DependencyInjection/ChillActivityExtension.php b/src/Bundle/ChillActivityBundle/DependencyInjection/ChillActivityExtension.php index e240bf17c..61354c7c8 100644 --- a/src/Bundle/ChillActivityBundle/DependencyInjection/ChillActivityExtension.php +++ b/src/Bundle/ChillActivityBundle/DependencyInjection/ChillActivityExtension.php @@ -55,7 +55,6 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf $loader->load('services/controller.yaml'); $loader->load('services/form.yaml'); $loader->load('services/templating.yaml'); - $loader->load('services/notification.yaml'); } public function prepend(ContainerBuilder $container) diff --git a/src/Bundle/ChillActivityBundle/config/services.yaml b/src/Bundle/ChillActivityBundle/config/services.yaml index 411ad1b5a..86168101a 100644 --- a/src/Bundle/ChillActivityBundle/config/services.yaml +++ b/src/Bundle/ChillActivityBundle/config/services.yaml @@ -1,4 +1,4 @@ -services: +services: chill.activity.security.authorization.activity_voter: class: Chill\ActivityBundle\Security\Authorization\ActivityVoter arguments: @@ -6,7 +6,7 @@ services: tags: - { name: security.voter } - { name: chill.role } - + chill.activity.security.authorization.activity_stats_voter: class: Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter arguments: @@ -14,8 +14,8 @@ services: tags: - { name: security.voter } - { name: chill.role } - - + + chill.activity.timeline: class: Chill\ActivityBundle\Timeline\TimelineActivityProvider arguments: @@ -33,3 +33,8 @@ services: autoconfigure: true resource: '../Menu/' tags: ['chill.menu_builder'] + + Chill\ActivityBundle\Notification\: + autowire: true + autoconfigure: true + resource: '../Notification' diff --git a/src/Bundle/ChillActivityBundle/config/services/notification.yaml b/src/Bundle/ChillActivityBundle/config/services/notification.yaml deleted file mode 100644 index 3db168ecd..000000000 --- a/src/Bundle/ChillActivityBundle/config/services/notification.yaml +++ /dev/null @@ -1,4 +0,0 @@ -services: - Chill\ActivityBundle\Notification\ActivityNotificationRenderer: - autoconfigure: true - autowire: true From 4646795c2cd8ca3871d9b70984bf813d4385be0e Mon Sep 17 00:00:00 2001 From: Marc Ducobu Date: Fri, 23 Jul 2021 10:18:04 +0200 Subject: [PATCH 21/24] Automatic styling correction --- .../Test/Export/AbstractExportTest.php | 168 +++++++++--------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php b/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php index e480ab145..9909a683b 100644 --- a/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php +++ b/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php @@ -26,7 +26,7 @@ use Symfony\Component\HttpFoundation\Request; /** * This class provide a set of tests for exports. - * + * * The tests provided by this class will check basic things, like * the type of value are conform to the expected, etc. * @@ -34,45 +34,45 @@ use Symfony\Component\HttpFoundation\Request; */ abstract class AbstractExportTest extends WebTestCase { - + use PrepareClientTrait; - + /** * Create an instance of the report to test - * + * * @return \Chill\MainBundle\Export\ExportInterface an instance of the export to test */ public abstract function getExport(); - + /** * Create possible combinaison of data (produced by the form). - * + * * This data will be used to generate data providers using this data. - * + * * @return array an array of data. Example : `array( array(), array('fields' => array(1,2,3), ...)` where an empty array and `array(1,2,3)` are possible values */ public abstract function getFormData(); - + /** - * get the possible modifiers which could apply in combination to this + * get the possible modifiers which could apply in combination to this * export. - * . - * - * @return array of string[] an array which contains an array of possible modifiers. Example : `array( array('modifier_1', 'modifier_2'), array('modifier_1'), ...)` + * . + * + * @return array of string[] an array which contains an array of possible modifiers. Example : `array( array('modifier_1', 'modifier_2'), array('modifier_1'), ...)` */ abstract public function getModifiersCombination(); - + /** * Return an array usable as ACL - * + * * If this method is overridden, the returned result must be an array * with this form : - * + * * ``` * array( - * array( - * 'center' => //center instance + * array( + * 'center' => //center instance * 'circles' => array(// array of circles instances ) * ) * ); @@ -84,15 +84,15 @@ abstract class AbstractExportTest extends WebTestCase if (static::$kernel === null) { static::bootKernel(); } - + $em = static::$kernel->getContainer() ->get('doctrine.orm.entity_manager'); - + $centers = $em->getRepository('ChillMainBundle:Center') ->findAll(); $circles = $em->getRepository('ChillMainBundle:Scope') ->findAll(); - + if (count($centers) === 0) { throw new \RuntimeException("No center found. Did you forget to " . "run `doctrine:fixtures:load` command before ?"); @@ -101,7 +101,7 @@ abstract class AbstractExportTest extends WebTestCase throw new \RuntimeException("No circle found. Did you forget to " . "run `doctrine:fixtures:load` command before ?"); } - + return array([ 'center' => $centers[0], 'circles' => [ @@ -115,204 +115,204 @@ abstract class AbstractExportTest extends WebTestCase public function testGetType() { $export = $this->getExport(); - - $this->assertInternalType('string', $export->getType(), + + $this->assertInternalType('string', $export->getType(), "Assert that the `getType` method return a string"); $this->assertNotEmpty($export->getType(), "Assert that the `getType` method" . " does not return an empty string."); } - + /** * Test that the description is not empty */ public function testGetDescription() { $export = $this->getExport(); - - $this->assertInternalType('string', $export->getDescription(), + + $this->assertInternalType('string', $export->getDescription(), "Assert that the `getDescription` method return a string"); - $this->assertNotEmpty($export->getDescription(), + $this->assertNotEmpty($export->getDescription(), "Assert that the `getDescription` method does not return an empty " . "string."); } - + /** * create data for `ìnitiateQuery` method */ public function dataProviderInitiateQuery() { $acl = $this->getAcl(); - + foreach($this->getModifiersCombination() as $modifiers) { - + foreach($this->getFormData() as $data) { - + yield array($modifiers, $acl, $data); } } } - + public function dataProviderGetQueryKeys() { foreach($this->getFormData() as $data) { yield array($data); } } - + /** - * + * * test that the query returned is a QueryBuilder or a NativeQuery. - * + * * If the query is a QueryBuilder, test that select and from is not empty. - * - * If the query is a native sql, test the query is not empty (length is + * + * If the query is a native sql, test the query is not empty (length is * > 0). - * + * * @dataProvider dataProviderInitiateQuery */ public function testInitiateQuery($modifiers, $acl, $data) { var_dump($data); $query = $this->getExport()->initiateQuery($modifiers, $acl, $data); - + $this->assertTrue($query instanceof QueryBuilder || $query instanceof NativeQuery, - sprintf("Assert that the returned query is an instance of %s or %s", + sprintf("Assert that the returned query is an instance of %s or %s", QueryBuilder::class, Query::class)); - + if ($query instanceof QueryBuilder) { - + $this->assertGreaterThanOrEqual(1, count($query->getDQLPart('select')), "assert there is at least one 'select' part"); - + $this->assertGreaterThanOrEqual(1, count($query->getDQLPart('from')), "assert there is at least one 'from' part"); - + } elseif ($query instanceof NativeQuery) { - $this->assertNotEmpty($query->getSQL(), + $this->assertNotEmpty($query->getSQL(), "check that the SQL query is not empty"); } } - + /** * Test that supportsModifier return : - * + * * - an array of string, if the query is a QueryBuilder ; * - nothing, if the query is a native SQL - * + * * @dataProvider dataProviderInitiateQuery */ public function testSupportsModifier($modifiers, $acl, $data) { $export = $this->getExport(); $query = $export->initiateQuery($modifiers, $acl, $data); - + if ($query instanceof QueryBuilder) { $this->assertContainsOnly('string', $export->supportsModifiers(), "Test that the `supportsModifiers` method returns only strings"); } elseif ($query instanceof NativeQuery) { - $this->assertTrue($export->supportsModifiers() === null || + $this->assertTrue($export->supportsModifiers() === null || count($export->supportsModifiers()) === 0, "Test that the `supportsModifier` methods returns null or an empty array"); } } - + /** * Test required role is an instance of Role */ public function testRequiredRole() { $role = $this->getExport()->requiredRole(); - - $this->assertInstanceOf(Role::class, $role, + + $this->assertInstanceOf(Role::class, $role, sprintf("test that the returned value of `requiredRole` is an instance " . "of %s", Role::class)); } - + /** * Test the formatters type are string */ public function testGetAllowedFormattersType() { $formattersTypes = $this->getExport()->getAllowedFormattersTypes(); - + $this->assertContainsOnly("string", $formattersTypes, "Test that the method `getAllowedFormattersTypes` returns an array of string"); } - + /** * Test that the query keys are strings - * + * * @param array $data * @dataProvider dataProviderGetQueryKeys */ public function testGetQueryKeys(array $data) { $queryKeys = $this->getExport()->getQueryKeys($data); - + $this->assertContainsOnly("string", $queryKeys, "test that the query keys returned by `getQueryKeys` are only strings"); $this->assertGreaterThanOrEqual(1, count($queryKeys), "test that there are at least one query key returned"); } - + /** - * - * Test that - * + * + * Test that + * * - the results have a correct form (are arrays or traversable) * - each key in a row are present in getQueryKeys ; * - each returned object of the `getLabels` method is callable * - each result can be converted to string using this callable * - each of this callable can provide a string for '_header' - * + * * @param string[] $modifiers * @param array $acl * @param array $data - * + * * @dataProvider dataProviderInitiateQuery */ public function testGetResultsAndLabels($modifiers, $acl, array $data) { // it is more convenient to group the `getResult` and `getLabels` test // due to the fact that testing both methods use the same tools. - + $queryKeys = $this->getExport()->getQueryKeys($data); $query = $this->getExport()->initiateQuery($modifiers, $acl, $data); - + // limit the result for the query for performance reason (only for QueryBuilder, // not possible in NativeQuery) if ($query instanceof QueryBuilder) { $query->setMaxResults(1); - } - + } + $results = $this->getExport()->getResult($query, $data); - - $this->assertInternalType('array', $results, + + $this->assertInternalType('array', $results, "assert that the returned result is an array"); - + if (count($results) === 0) { $this->markTestIncomplete("The result is empty. We cannot process tests " . "on results"); } - + // testing the result $result = $results[0]; - + $this->assertTrue( $result instanceof \Traversable || is_array($result), "test that each row in the result is traversable or an array"); - + foreach ($result as $key => $value) { $this->assertContains($key, $queryKeys, "test that each key is present in `getQueryKeys`"); - + $closure = $this->getExport()->getLabels($key, array($value), $data); - + $this->assertTrue(is_callable($closure, false), "test that the `getLabels` for key is a callable"); $this->assertTrue(is_string((string) call_user_func($closure, $value)), sprintf("test that the callable return by `getLabels` for key %s " . "is a string or an be converted to a string", $key)); - + $this->assertTrue( // conditions is_string((string) call_user_func($closure, '_header')) @@ -322,13 +322,13 @@ abstract class AbstractExportTest extends WebTestCase sprintf("Test that the callable return by `getLabels` for key %s " . "can provide an header", $key) ); - } - + } + } - + /** - * Test that the translated title of the export is present the list, + * Test that the translated title of the export is present the list, * and that the list of exports (under `/fr/exports/`) is still successfull */ public function testListExportPage() @@ -338,17 +338,17 @@ abstract class AbstractExportTest extends WebTestCase $export = $this->getExport(); $prophet= new \Prophecy\Prophet; $container = static::$kernel->getContainer(); - + // store the locale in a request $request = new Request(); $request->setLocale('fr'); $container->get('request_stack')->push($request); // translate the title $title = $container->get('translator')->trans($export->getTitle()); - + // performs the request to /fr/exports $crawler = $client->request('GET', '/fr/exports/'); - + // and finally make tests $this->assertTrue($client->getResponse()->isSuccessful(), "test that the response of /fr/exports/ is successful"); From 9d478c0f0103b6a67ea114344cef02e88b8d3597 Mon Sep 17 00:00:00 2001 From: Marc Ducobu Date: Fri, 23 Jul 2021 10:18:41 +0200 Subject: [PATCH 22/24] Removing var_dump --- src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php b/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php index 9909a683b..fcf82fca9 100644 --- a/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php +++ b/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php @@ -172,7 +172,6 @@ abstract class AbstractExportTest extends WebTestCase */ public function testInitiateQuery($modifiers, $acl, $data) { - var_dump($data); $query = $this->getExport()->initiateQuery($modifiers, $acl, $data); $this->assertTrue($query instanceof QueryBuilder || $query instanceof NativeQuery, From 9e9a459a20e3f2c04abca1b441fd2da631a846aa Mon Sep 17 00:00:00 2001 From: Marc Ducobu Date: Tue, 27 Jul 2021 15:59:03 +0200 Subject: [PATCH 23/24] Replace chill_main.paginator_factory by PaginatorFactory::class --- src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php index 25b7e5860..ca28b1d14 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php @@ -1141,7 +1141,7 @@ class CRUDController extends AbstractController */ protected function getPaginatorFactory(): PaginatorFactory { - return $this->container->get('chill_main.paginator_factory'); + return $this->container->get(PaginatorFactory::class); } /** From f734210db52381af1c5fd15c7e264e7812dc30fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 30 Jul 2021 13:40:52 +0200 Subject: [PATCH 24/24] fix error after merge --- .../Repository/SocialWork/ResultRepository.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php index d2755e3ce..33bd8f7ef 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php @@ -32,11 +32,6 @@ final class ResultRepository implements ObjectRepository return $this->repository->findAll(); } - public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null) - { - return $this->repository->findBy($criteria, $orderBy, $limit, $offset); - } - /** * * @return Result[]