mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-11-04 03:08:25 +00:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
			404-action
			...
			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