Publier Globinours 1.0.0-rc.3

This commit is contained in:
Alexandre NOEL 2026-09-03 12:39:15 +02:00
commit 9a2b4068da
325 changed files with 38230 additions and 20 deletions

View file

@ -0,0 +1,164 @@
<?php
declare(strict_types=1);
final class Asm3ContactPlanner
{
public static function plan(string $path, PDO $db): array
{
$source = Asm3Analyzer::rows($path, ['owner', 'animal', 'adoption', 'animalvaccination']);
$adopters = [];
$depositors = [];
$vets = [];
foreach ($source['adoption'] ?? [] as $r) {
if ((int) ($r['OWNERID'] ?? 0) > 0) {
$adopters[(int) $r['OWNERID']] = true;
}
}
foreach ($source['animal'] ?? [] as $r) {
foreach (['BROUGHTINBYOWNERID', 'ORIGINALOWNERID'] as $field) {
if ((int) ($r[$field] ?? 0) > 0) {
$depositors[(int) $r[$field]] = true;
}
}
foreach (['CURRENTVETID', 'OWNERSVETID'] as $field) {
if ((int) ($r[$field] ?? 0) > 0) {
$vets[(int) $r[$field]] = true;
}
}
}
foreach ($source['animalvaccination'] ?? [] as $r) {
if ((int) ($r['ADMINISTERINGVETID'] ?? 0) > 0) {
$vets[(int) $r['ADMINISTERINGVETID']] = true;
}
}
$existing = $db
->query('SELECT id,kind,name,phone,email,city FROM directory_contacts WHERE deleted_at IS NULL')
->fetchAll(PDO::FETCH_ASSOC);
$items = [];
$summary = ['create' => 0, 'match' => 0, 'review' => 0];
foreach ($source['owner'] ?? [] as $row) {
$mapped = self::map(
$row,
isset($adopters[(int) $row['ID']]),
isset($depositors[(int) $row['ID']]),
isset($vets[(int) $row['ID']]),
);
$strong = [];
$weak = [];
$reasons = [];
foreach ($existing as $contact) {
$email = self::text($mapped['email']);
$phone = self::phone($mapped['phone']);
$sameEmail = $email !== '' && $email === self::text($contact['email']);
$samePhone = $phone !== '' && $phone === self::phone($contact['phone']);
$sameIdentity =
self::text($mapped['name']) === self::text($contact['name']) &&
self::text($mapped['city']) === self::text($contact['city']);
if ($sameEmail || $samePhone || $sameIdentity) {
$strong[(int) $contact['id']] = $contact;
if ($sameEmail) {
$reasons[] = 'même adresse e-mail';
}
if ($samePhone) {
$reasons[] = 'même téléphone';
}
if ($sameIdentity) {
$reasons[] = 'même nom et ville';
}
} elseif (
self::text($mapped['name']) !== '' &&
self::text($mapped['name']) === self::text($contact['name'])
) {
$weak[(int) $contact['id']] = $contact;
}
}
if (count($strong) === 1) {
$action = 'match';
$candidates = $strong;
} elseif (count($strong) > 1) {
$action = 'review';
$candidates = $strong;
} elseif ($weak) {
$action = 'review';
$candidates = $weak;
$reasons[] = 'même nom uniquement';
} else {
$action = 'create';
$candidates = [];
}
$summary[$action]++;
$items[] = [
'asm3_id' => (int) $row['ID'],
'action' => $action,
'match_reasons' => array_values(array_unique($reasons)),
'globinours_candidates' => array_values(
array_map(
static fn($c) => ['id' => (int) $c['id'], 'name' => $c['name'], 'city' => $c['city']],
$candidates,
),
),
'mapped' => $mapped,
];
}
return ['source' => basename($path), 'read_only' => true, 'summary' => $summary, 'items' => $items];
}
private static function map(array $r, bool $adopter, bool $depositor, bool $vetReferenced): array
{
$yes = static fn($v) => (string) $v === '1';
$name = trim(
(string) ($r['OWNERNAME'] ?? '' ?:
trim((string) ($r['OWNERFORENAMES'] ?? '') . ' ' . (string) ($r['OWNERSURNAME'] ?? ''))),
);
$organization =
(bool) preg_match('/\b(cabinet|clinique|cr[eé]matorium|association|refuge|fourri[eè]re)\b/iu', $name) ||
$yes($r['ISSHELTER'] ?? 0) ||
$yes($r['ISSUPPLIER'] ?? 0);
$roles = [];
if ($adopter || $yes($r['ISADOPTER'] ?? 0)) {
$roles[] = 'adoptant';
}
if ($yes($r['ISFOSTERER'] ?? 0)) {
$roles[] = 'fa';
}
if ($yes($r['ISVOLUNTEER'] ?? 0)) {
$roles[] = 'benevole';
}
if ($depositor) {
$roles[] = 'deposant';
}
if ($vetReferenced || $yes($r['ISVET'] ?? 0)) {
$roles[] = $organization ? 'cabinet' : 'veterinaire';
}
if (preg_match('/cr[eé]matorium/iu', $name)) {
$roles[] = 'crematorium';
}
$phones = array_values(
array_filter([
trim((string) ($r['MOBILETELEPHONE'] ?? '')),
trim((string) ($r['HOMETELEPHONE'] ?? '')),
trim((string) ($r['WORKTELEPHONE'] ?? '')),
]),
);
return [
'kind' => $organization ? 'organization' : 'person',
'name' => $name ?: 'Contact ASM3 ' . (int) $r['ID'],
'phone' => $phones[0] ?? null,
'email' => trim((string) ($r['EMAILADDRESS'] ?? '')) ?: null,
'address' => trim((string) ($r['OWNERADDRESS'] ?? '')) ?: null,
'postal_code' => trim((string) ($r['OWNERPOSTCODE'] ?? '')) ?: null,
'city' => trim((string) ($r['OWNERTOWN'] ?? '')) ?: null,
'country' => trim((string) ($r['OWNERCOUNTRY'] ?? '')) ?: 'France',
'notes' => trim((string) ($r['COMMENTS'] ?? '')) ?: null,
'roles' => array_values(array_unique($roles)),
];
}
private static function text(mixed $v): string
{
return mb_strtolower(trim((string) $v));
}
private static function phone(mixed $v): string
{
return (string) preg_replace('/\D+/', '', (string) $v);
}
}