mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-10-25 14:42:48 +00:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
			389-displa
			...
			issue451_m
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | bfb19d0670 | ||
|  | 574c3596d1 | ||
|  | d7f5de6570 | ||
|  | bc08719c0c | ||
|  | a3ffd2709d | 
| @@ -11,7 +11,8 @@ and this project adheres to | ||||
| ## Unreleased | ||||
|  | ||||
| <!-- write down unreleased development here --> | ||||
|  | ||||
| * [person] use the same rendering of person in similar person proposition (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/348) | ||||
| * [person] move similar person matcher to PersonACLAwareRepository and replace the SimilarPersonMatcher uses (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/451) | ||||
| ## Test releases | ||||
|  | ||||
| ### test release 2022-02-14 | ||||
| @@ -22,7 +23,7 @@ and this project adheres to | ||||
| * [person] accompanying course work: fix on-the-fly update of thirdParty | ||||
| * fix normalisation of accompanying course requestor api (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/378) | ||||
| * [person] add a returnPath when clicking on some Person or ThirdParty badge (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/427) | ||||
| * [person] accompanying course work: fix on-the-fly update of thirdParty  | ||||
| * [person] accompanying course work: fix on-the-fly update of thirdParty | ||||
| * [on-the-fly] close modal only after validation | ||||
| * [person] correct thirdparty PATCH url + add email and altnames in AddPerson and serializer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/433) | ||||
| * change order for accompanying course work list | ||||
|   | ||||
| @@ -16,8 +16,8 @@ use Chill\PersonBundle\Entity\Person; | ||||
| use Chill\PersonBundle\Form\CreationPersonType; | ||||
| use Chill\PersonBundle\Form\PersonType; | ||||
| use Chill\PersonBundle\Privacy\PrivacyEvent; | ||||
| use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface; | ||||
| use Chill\PersonBundle\Repository\PersonRepository; | ||||
| use Chill\PersonBundle\Search\SimilarPersonMatcher; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Psr\Log\LoggerInterface; | ||||
| use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | ||||
| @@ -56,11 +56,6 @@ final class PersonController extends AbstractController | ||||
|      */ | ||||
|     protected $personRepository; | ||||
|  | ||||
|     /** | ||||
|      * @var SimilarPersonMatcher | ||||
|      */ | ||||
|     protected $similarPersonMatcher; | ||||
|  | ||||
|     /** | ||||
|      * @var TranslatorInterface | ||||
|      */ | ||||
| @@ -76,13 +71,14 @@ final class PersonController extends AbstractController | ||||
|      */ | ||||
|     private $logger; | ||||
|  | ||||
|     private PersonACLAwareRepositoryInterface $personACLAwareRepository; | ||||
|  | ||||
|     /** | ||||
|      * @var ValidatorInterface | ||||
|      */ | ||||
|     private $validator; | ||||
|  | ||||
|     public function __construct( | ||||
|         SimilarPersonMatcher $similarPersonMatcher, | ||||
|         TranslatorInterface $translator, | ||||
|         EventDispatcherInterface $eventDispatcher, | ||||
|         PersonRepository $personRepository, | ||||
| @@ -90,9 +86,9 @@ final class PersonController extends AbstractController | ||||
|         LoggerInterface $logger, | ||||
|         ValidatorInterface $validator, | ||||
|         EntityManagerInterface $em, | ||||
|         Security $security | ||||
|         Security $security, | ||||
|         PersonACLAwareRepositoryInterface $personACLAwareRepository | ||||
|     ) { | ||||
|         $this->similarPersonMatcher = $similarPersonMatcher; | ||||
|         $this->translator = $translator; | ||||
|         $this->eventDispatcher = $eventDispatcher; | ||||
|         $this->configPersonAltNameHelper = $configPersonAltNameHelper; | ||||
| @@ -101,6 +97,7 @@ final class PersonController extends AbstractController | ||||
|         $this->validator = $validator; | ||||
|         $this->em = $em; | ||||
|         $this->security = $security; | ||||
|         $this->personACLAwareRepository = $personACLAwareRepository; | ||||
|     } | ||||
|  | ||||
|     public function editAction($person_id, Request $request) | ||||
| @@ -236,8 +233,8 @@ final class PersonController extends AbstractController | ||||
|             $request->getMethod() === Request::METHOD_POST | ||||
|             && $form->isValid() | ||||
|         ) { | ||||
|             $alternatePersons = $this->similarPersonMatcher | ||||
|                 ->matchPerson($person); | ||||
|             $alternatePersons = $this->personACLAwareRepository | ||||
|                 ->findMatchingPersons($person); | ||||
|  | ||||
|             if ( | ||||
|                 false === $this->isLastPostDataChanges($form, $request, true) | ||||
|   | ||||
| @@ -20,9 +20,9 @@ use Chill\PersonBundle\Entity\PersonNotDuplicate; | ||||
| use Chill\PersonBundle\Form\PersonConfimDuplicateType; | ||||
| use Chill\PersonBundle\Form\PersonFindManuallyDuplicateType; | ||||
| use Chill\PersonBundle\Privacy\PrivacyEvent; | ||||
| use Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface; | ||||
| use Chill\PersonBundle\Repository\PersonNotDuplicateRepository; | ||||
| use Chill\PersonBundle\Repository\PersonRepository; | ||||
| use Chill\PersonBundle\Search\SimilarPersonMatcher; | ||||
| use Chill\TaskBundle\Entity\SingleTask; | ||||
| use http\Exception\InvalidArgumentException; | ||||
| use Symfony\Bundle\FrameworkBundle\Controller\Controller; | ||||
| @@ -39,6 +39,11 @@ class PersonDuplicateController extends Controller | ||||
|      */ | ||||
|     private $eventDispatcher; | ||||
|  | ||||
|     /** | ||||
|      * @var PersonACLAwareRepositoryInterface | ||||
|      */ | ||||
|     private $personACLAwareRepository; | ||||
|  | ||||
|     /** | ||||
|      * @var \Chill\PersonBundle\Actions\Remove\PersonMove | ||||
|      */ | ||||
| @@ -49,24 +54,19 @@ class PersonDuplicateController extends Controller | ||||
|      */ | ||||
|     private $personRepository; | ||||
|  | ||||
|     /** | ||||
|      * @var \Chill\PersonBundle\Search\SimilarPersonMatcher | ||||
|      */ | ||||
|     private $similarPersonMatcher; | ||||
|  | ||||
|     /** | ||||
|      * @var \Symfony\Component\Translation\TranslatorInterface | ||||
|      */ | ||||
|     private $translator; | ||||
|  | ||||
|     public function __construct( | ||||
|         SimilarPersonMatcher $similarPersonMatcher, | ||||
|         PersonACLAwareRepositoryInterface $personACLAwareRepository, | ||||
|         TranslatorInterface $translator, | ||||
|         PersonRepository $personRepository, | ||||
|         PersonMove $personMove, | ||||
|         EventDispatcherInterface $eventDispatcher | ||||
|     ) { | ||||
|         $this->similarPersonMatcher = $similarPersonMatcher; | ||||
|         $this->personACLAwareRepository = $personACLAwareRepository; | ||||
|         $this->translator = $translator; | ||||
|         $this->personRepository = $personRepository; | ||||
|         $this->personMove = $personMove; | ||||
| @@ -246,8 +246,11 @@ class PersonDuplicateController extends Controller | ||||
|             'You are not allowed to see this person.' | ||||
|         ); | ||||
|  | ||||
|         $duplicatePersons = $this->similarPersonMatcher-> | ||||
|             matchPerson($person, $personNotDuplicateRepository, 0.5, SimilarPersonMatcher::SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL); | ||||
|         $duplicatePersons = $this->personACLAwareRepository->findMatchingPersons( | ||||
|             $person, | ||||
|             0.5, | ||||
|             PersonACLAwareRepositoryInterface::SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL | ||||
|         ); | ||||
|  | ||||
|         $notDuplicatePersons = $personNotDuplicateRepository->findNotDuplicatePerson($person); | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,7 @@ use Chill\MainBundle\Search\SearchApiQuery; | ||||
| use Chill\MainBundle\Security\Authorization\AuthorizationHelper; | ||||
| use Chill\PersonBundle\Entity\Person; | ||||
| use Chill\PersonBundle\Security\Authorization\PersonVoter; | ||||
| use Chill\PersonBundle\Templating\Entity\PersonRender; | ||||
| use DateTimeInterface; | ||||
| use Doctrine\ORM\EntityManagerInterface; | ||||
| use Doctrine\ORM\NonUniqueResultException; | ||||
| @@ -40,18 +41,26 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac | ||||
|  | ||||
|     private EntityManagerInterface $em; | ||||
|  | ||||
|     private PersonNotDuplicateRepository $personNotDuplicateRepository; | ||||
|  | ||||
|     private PersonRender $personRender; | ||||
|  | ||||
|     private Security $security; | ||||
|  | ||||
|     public function __construct( | ||||
|         Security $security, | ||||
|         EntityManagerInterface $em, | ||||
|         CountryRepository $countryRepository, | ||||
|         AuthorizationHelper $authorizationHelper | ||||
|         AuthorizationHelper $authorizationHelper, | ||||
|         PersonRender $personRender, | ||||
|         PersonNotDuplicateRepository $personNotDuplicateRepository | ||||
|     ) { | ||||
|         $this->security = $security; | ||||
|         $this->em = $em; | ||||
|         $this->countryRepository = $countryRepository; | ||||
|         $this->authorizationHelper = $authorizationHelper; | ||||
|         $this->personRender = $personRender; | ||||
|         $this->personNotDuplicateRepository = $personNotDuplicateRepository; | ||||
|     } | ||||
|  | ||||
|     public function buildAuthorizedQuery( | ||||
| @@ -297,6 +306,70 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac | ||||
|         return $this->fetchQueryPerson($query); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @throws NonUniqueResultException | ||||
|      * @throws ParsingException | ||||
|      * | ||||
|      * @return array|Person[] | ||||
|      */ | ||||
|     public function findMatchingPersons( | ||||
|         Person $person, | ||||
|         float $precision = 0.15, | ||||
|         string $orderBy = self::SIMILAR_SEARCH_ORDER_BY_SIMILARITY | ||||
|     ): array { | ||||
|         $query = $this->matchPerson($person, $precision, $orderBy); | ||||
|         $authorizedQuery = $this->addAuthorizations($query); | ||||
|  | ||||
|         return $this->fetchQueryPerson($authorizedQuery); | ||||
|     } | ||||
|  | ||||
|     public function matchPerson( | ||||
|         Person $person, | ||||
|         float $precision = 0.15, | ||||
|         string $orderBy = self::SIMILAR_SEARCH_ORDER_BY_SIMILARITY | ||||
|     ): SearchApiQuery { | ||||
|         $fullName = $this->personRender->renderString($person, []); | ||||
|  | ||||
|         $query = new SearchApiQuery(); | ||||
|         $query->setFromClause('chill_person_person AS person'); | ||||
|         $query->andWhereClause( | ||||
|             'SIMILARITY(person.fullnameCanonical, UNACCENT(LOWER(?))) >= ?', | ||||
|             [$fullName, $precision] | ||||
|         ); | ||||
|  | ||||
|         if (null !== $person->getId()) { | ||||
|             $query->andWhereClause( | ||||
|                 'person.id != ?', | ||||
|                 [$person->getId()] | ||||
|             ); | ||||
|  | ||||
|             $notDuplicatePersons = $this->personNotDuplicateRepository->findNotDuplicatePerson($person); | ||||
|  | ||||
|             if (count($notDuplicatePersons)) { | ||||
|                 $query->andWhereClause( | ||||
|                     'person.id NOT IN (?)', | ||||
|                     [$notDuplicatePersons] | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         switch ($orderBy) { | ||||
|             case self::SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL: | ||||
|                 $query->setSelectPertinence('person.fullnameCanonical'); | ||||
|  | ||||
|                 break; | ||||
|  | ||||
|             case self::SIMILAR_SEARCH_ORDER_BY_SIMILARITY: | ||||
|             default: | ||||
|                 $query->setSelectPertinence( | ||||
|                     'SIMILARITY(person.fullnameCanonical, UNACCENT(LOWER(?)))', | ||||
|                     [$fullName] | ||||
|                 ); | ||||
|         } | ||||
|  | ||||
|         return $query; | ||||
|     } | ||||
|  | ||||
|     private function addAuthorizations(SearchApiQuery $query): SearchApiQuery | ||||
|     { | ||||
|         $authorizedCenters = $this->authorizationHelper | ||||
|   | ||||
| @@ -17,6 +17,10 @@ use DateTimeInterface; | ||||
|  | ||||
| interface PersonACLAwareRepositoryInterface | ||||
| { | ||||
|     public const SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL = 'alphabetical'; | ||||
|  | ||||
|     public const SIMILAR_SEARCH_ORDER_BY_SIMILARITY = 'similarity'; | ||||
|  | ||||
|     public function buildAuthorizedQuery( | ||||
|         ?string $default = null, | ||||
|         ?string $firstname = null, | ||||
| @@ -61,4 +65,13 @@ interface PersonACLAwareRepositoryInterface | ||||
|         ?string $phonenumber = null, | ||||
|         ?string $city = null | ||||
|     ): array; | ||||
|  | ||||
|     /** | ||||
|      * @return array|Person[] | ||||
|      */ | ||||
|     public function findMatchingPersons( | ||||
|         Person $person, | ||||
|         float $precision = 0.15, | ||||
|         string $orderBy = self::SIMILAR_SEARCH_ORDER_BY_SIMILARITY | ||||
|     ): array; | ||||
| } | ||||
|   | ||||
| @@ -17,6 +17,15 @@ | ||||
|  | ||||
| {% block title %}{{ 'Add a person'|trans }}{% endblock title %} | ||||
|  | ||||
| {% macro button_person_after(person) %} | ||||
|     {% set household = person.getCurrentHousehold %} | ||||
|     {% if household is not null %} | ||||
|         <li> | ||||
|             <a href="{{ path('chill_person_household_summary', { 'household_id': household.id }) }}" class="btn btn-sm btn-chill-beige"><i class="fa fa-home"></i></a> | ||||
|         </li> | ||||
|     {% endif %} | ||||
| {% endmacro %} | ||||
|  | ||||
| {% block content %} | ||||
| <div class="col-md-10 col-xxl person-new"> | ||||
|  | ||||
| @@ -31,34 +40,24 @@ | ||||
|             </span> | ||||
|         </div> | ||||
|  | ||||
|         <table class="table table-bordered border-dark"> | ||||
|             <thead> | ||||
|             <tr> | ||||
|                 <th class="chill-red">{{ 'Name'|trans }}</th> | ||||
|                 <th class="chill-green">{{ 'Date of birth'|trans }}</th> | ||||
|                 <th class="chill-orange">{{ 'Nationality'|trans }}</th> | ||||
|             </tr> | ||||
|             </thead> | ||||
|             <tbody> | ||||
|         <div class="flex-table"> | ||||
|             {% for person in alternatePersons %} | ||||
|                 <tr> | ||||
|                     <td> | ||||
|                         <a href="{{ path('chill_person_view', {'person_id': person.id } ) }}"> | ||||
|                             {{ person|chill_entity_render_string }}{% apply spaceless %} | ||||
|                                 {% if person.isOpen == false %} | ||||
|                                     <i class="icon-lock"></i> | ||||
|                                 {% endif %} | ||||
|                             {% endapply %} | ||||
|                         </a> | ||||
|                     </td> | ||||
|                     <td>{% if person.birthdate is not null %}{{ person.birthdate|format_date('long') }}{% else %} {% endif %}</td> | ||||
|                     <td> | ||||
|                         {% if person.nationality is not null %}{{ person.nationality.name|localize_translatable_string }}{%  else  %}{{ 'Without nationality'|trans }}{% endif %} | ||||
|                     </td> | ||||
|                 </tr> | ||||
|             <div class="item-bloc"> | ||||
|  | ||||
|                 {{ person|chill_entity_render_box({ | ||||
|                     'render': 'bloc', | ||||
|                     'addLink': true, | ||||
|                     'addInfo': true, | ||||
|                     'addAge': true, | ||||
|                     'addAltNames': true, | ||||
|                     'addCenter': true, | ||||
|                     'address_multiline': false, | ||||
|                     'customButtons': { 'after': _self.button_person_after(person) } | ||||
|                 }) }} | ||||
|             </div> | ||||
|             {% endfor %} | ||||
|             </tbody> | ||||
|         </table> | ||||
|         </div> | ||||
|  | ||||
|     {% endif %} | ||||
|  | ||||
|     {{ form_start(form, {'attr' : {'id' : 'create-form'}}) }} | ||||
|   | ||||
| @@ -21,6 +21,9 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt | ||||
|  | ||||
| use function count; | ||||
|  | ||||
| /** | ||||
|  * @deprecated | ||||
|  */ | ||||
| class SimilarPersonMatcher | ||||
| { | ||||
|     public const SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL = 'alphabetical'; | ||||
|   | ||||
| @@ -24,7 +24,7 @@ services: | ||||
|  | ||||
|     Chill\PersonBundle\Controller\PersonDuplicateController: | ||||
|         arguments: | ||||
|             $similarPersonMatcher: '@Chill\PersonBundle\Search\SimilarPersonMatcher' | ||||
|             $personACLAwareRepository: '@Chill\PersonBundle\Repository\PersonACLAwareRepositoryInterface' | ||||
|             $translator: '@Symfony\Component\Translation\TranslatorInterface' | ||||
|             $personRepository: '@Chill\PersonBundle\Repository\PersonRepository' | ||||
|             $personMove: '@Chill\PersonBundle\Actions\Remove\PersonMove' | ||||
|   | ||||
		Reference in New Issue
	
	Block a user