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,536 @@
<?php
declare(strict_types=1);
final class DemoDataService
{
private const TABLES = [
'agenda_volunteers',
'agenda_items',
'prescription_treatments',
'lab_results',
'medical_prescriptions',
'lab_reports',
'treatment_administrations',
'care_round_observations',
'care_rounds',
'medical_photos',
'animal_photos',
'animal_expenses',
'animal_surgeries',
'animal_health_conditions',
'dewormings',
'vaccinations',
'treatments',
'medical_notes',
'measurements',
'animal_placements',
'adoptions',
'animal_movements',
'animal_deaths',
'animal_location_history',
'animal_history',
'litter_kittens',
'litters',
'bonded_group_members',
'bonded_groups',
'icad_cache',
'asm3_import_links',
'asm3_import_runs',
'asm3_import_jobs',
'directory_contact_roles',
'animals',
'directory_contacts',
'grant_documents',
'grant_applications',
'grant_years',
'care_room_layouts',
'clinics',
'geocode_cache',
'audit_log',
];
public static function reset(PDO $db): array
{
$before = [
'animals' => (int) $db->query('SELECT COUNT(*) FROM animals')->fetchColumn(),
'contacts' => (int) $db->query('SELECT COUNT(*) FROM directory_contacts')->fetchColumn(),
];
$sequence = $db->prepare('DELETE FROM sqlite_sequence WHERE name=?');
$exists = $db->prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?");
foreach (self::TABLES as $table) {
$exists->execute([$table]);
if (!$exists->fetchColumn()) {
continue;
}
$db->exec('DELETE FROM ' . $table);
$sequence->execute([$table]);
}
return $before;
}
public static function seed(PDO $db, ?int $userId): array
{
$contacts = [
'fa' => [
'person',
'Alice Martin',
'06 00 00 00 01',
'alice.demo@example.org',
'12 rue des Lilas',
'29260',
'Lesneven',
],
'adopter' => [
'person',
'Marc Durand',
'06 00 00 00 02',
'marc.demo@example.org',
'8 rue du Port',
'29200',
'Brest',
],
'depositor' => [
'person',
'Sophie Le Gall',
'06 00 00 00 03',
'sophie.demo@example.org',
'4 place du Marché',
'29000',
'Ville fictive',
],
'vet' => [
'person',
'Dr Camille Morel',
'02 00 00 00 04',
'veterinaire.demo@example.org',
'2 avenue des Chats',
'29260',
'Lesneven',
],
'clinic' => [
'organization',
'Clinique vétérinaire Démo',
'02 00 00 00 05',
'clinique.demo@example.org',
'2 avenue des Chats',
'29260',
'Lesneven',
],
'crematorium' => [
'organization',
'Crématorium animalier Démo',
'02 00 00 00 06',
'crematorium.demo@example.org',
'1 route du Souvenir',
'29400',
'Landivisiau',
],
];
$contactIds = [];
$insert = $db->prepare(
'INSERT INTO directory_contacts(kind,name,phone,email,address,postal_code,city,notes) VALUES(?,?,?,?,?,?,?,?)',
);
$role = $db->prepare('INSERT INTO directory_contact_roles(contact_id,role) VALUES(?,?)');
foreach ($contacts as $key => $c) {
$insert->execute([...$c, 'Donnée fictive de démonstration']);
$contactIds[$key] = (int) $db->lastInsertId();
$roleName = match ($key) {
'adopter' => 'adoptant',
'vet' => 'veterinaire',
'clinic' => 'cabinet',
'crematorium' => 'crematorium',
'depositor' => 'deposant',
default => 'fa',
};
$role->execute([$contactIds[$key], $roleName]);
}
$animals = [
[
'DEMO-001',
'Moka',
'F',
'2019-05-14',
'refuge',
'silver',
'Bicolore Noir / Blanc',
'yes',
'yes',
'yes',
'yes',
'yes',
'available',
],
[
'DEMO-002',
'Pixel',
'M',
'2021-09-03',
'refuge',
'twix',
'Roux',
'yes',
'yes',
'yes',
'yes',
'no',
'available',
],
[
'DEMO-003',
'Twix',
'M',
'2019-04-15',
'quarantaine',
null,
'Roux',
'yes',
'yes',
'yes',
'yes',
'yes',
'not_available',
],
[
'DEMO-004',
'Nox',
'M',
'2023-02-18',
'soin',
null,
'Noir',
'no',
'yes',
'unknown',
'yes',
'no',
'not_available',
],
[
'DEMO-005',
'Caramel',
'F',
'2020-07-21',
'fa',
null,
'Écaille de tortue',
'yes',
'yes',
'yes',
'yes',
'yes',
'available',
],
[
'DEMO-006',
'Plume',
'F',
'2022-04-08',
'refuge',
'silver',
'Tricolore / Calico',
'yes',
'yes',
'yes',
'yes',
'yes',
'available',
],
[
'DEMO-007',
'Plume-Bébé-1',
'F',
date('Y-m-d', strtotime('-4 months')),
'refuge',
'silver',
'Bicolore Gris / Blanc',
'unknown',
'yes',
'unknown',
'unknown',
'unknown',
'not_available',
],
[
'DEMO-008',
'Plume-Bébé-2',
'M',
date('Y-m-d', strtotime('-4 months')),
'refuge',
'silver',
'Gris',
'unknown',
'yes',
'unknown',
'unknown',
'unknown',
'not_available',
],
[
'DEMO-009',
'Plume-Bébé-3',
'U',
date('Y-m-d', strtotime('-4 months')),
'refuge',
'silver',
'Tigré / Tabby',
'unknown',
'yes',
'unknown',
'unknown',
'unknown',
'not_available',
],
[
'DEMO-010',
'Lune',
'F',
'2021-01-12',
'refuge',
'twix',
'Blanc',
'yes',
'no',
'yes',
'yes',
'yes',
'available',
],
[
'DEMO-011',
'Soleil',
'M',
'2021-01-12',
'refuge',
'twix',
'Roux',
'yes',
'no',
'yes',
'yes',
'yes',
'available',
],
[
'DEMO-012',
'Étoile',
'F',
'2012-06-01',
'decede',
null,
'Noir',
'yes',
'unknown',
'unknown',
'unknown',
'unknown',
'not_available',
],
];
$animalIds = [];
$animal = $db->prepare(
"INSERT INTO animals(internal_code,name,species,sex,birth_date,status,refuge_room,care_box_key,color,sterilized,sterilization_status,compatibility_cats,compatibility_dogs,compatibility_children,house_trained,adoption_availability,intake_date,intake_type,intake_reason,rescue_location_name,current_address,chip_id,identification_type,identification_registration_status,archived_at,notes) VALUES(:code,:name,'chat',:sex,:birth,:status,:room,:box,:color,:sterilized,:sterilization,:cats,:dogs,:children,:clean,:availability,date('now','-'||:days||' days'),'found','stray_found','Commune fictive','5 rue de la Démonstration, 29260 Lesneven',:chip,:identification,:registration,:archived,'Donnée fictive de démonstration')",
);
foreach ($animals as $i => $a) {
[
$code,
$name,
$sex,
$birth,
$status,
$room,
$color,
$sterilization,
$cats,
$dogs,
$children,
$clean,
$availability,
] = $a;
$sterilized = $sterilization === 'yes' ? 1 : 0;
$chip = $i < 6 ? '250269900' . str_pad((string) ($i + 1), 6, '0', STR_PAD_LEFT) : null;
$animal->execute([
':code' => $code,
':name' => $name,
':sex' => $sex,
':birth' => $birth,
':status' => $status,
':room' => $room,
':box' => $status === 'soin' ? 'care-top-1' : null,
':color' => $color,
':sterilized' => $sterilized,
':sterilization' => $sterilization,
':cats' => $cats,
':dogs' => $dogs,
':children' => $children,
':clean' => $clean,
':availability' => $availability,
':days' => 90 + $i * 12,
':chip' => $chip,
':identification' => $chip ? 'microchip' : 'unknown',
':registration' => $chip ? 'registered' : 'unknown',
':archived' => $status === 'decede' ? date('Y-m-d H:i:s') : null,
]);
$animalIds[$name] = (int) $db->lastInsertId();
}
$db->prepare('INSERT INTO litters(mother_id,birth_date,notes) VALUES(?,?,?)')->execute([
$animalIds['Plume'],
$animals[6][3],
'Portée fictive de démonstration',
]);
$litter = (int) $db->lastInsertId();
$link = $db->prepare('INSERT INTO litter_kittens(litter_id,animal_id,position) VALUES(?,?,?)');
foreach (['Plume-Bébé-1', 'Plume-Bébé-2', 'Plume-Bébé-3'] as $i => $name) {
$link->execute([$litter, $animalIds[$name], $i + 1]);
}
$db->prepare('INSERT INTO bonded_groups(name,notes) VALUES(?,?)')->execute([
'Duo Lune & Soleil',
'Chats fictifs à adopter ensemble',
]);
$group = (int) $db->lastInsertId();
$member = $db->prepare('INSERT INTO bonded_group_members(group_id,animal_id,position) VALUES(?,?,?)');
$member->execute([$group, $animalIds['Lune'], 1]);
$member->execute([$group, $animalIds['Soleil'], 2]);
$twix = $animalIds['Twix'];
$db->prepare(
"UPDATE animals SET breed='Européen',birth_is_estimated=0,fiv=0,felv=0,quarantine_until=date('now','+10 days'),intake_circumstances='Trouvé seul, bilan sanitaire programmé à larrivée.',depositor_contact_id=?,rescue_location_name='Commune fictive',current_address='Quarantaine — box supérieur gauche',identification_date=date('now','-2 years'),notes='Fiche vitrine entièrement fictive : elle illustre un dossier animal complet.' WHERE id=?",
)->execute([$contactIds['depositor'], $twix]);
$db->prepare(
"INSERT OR IGNORE INTO ref_medications(name,molecule,form,notes) VALUES('Doxybactin','doxycycline','comprimé','Référence de démonstration')",
)->execute();
$med = (int) $db->query("SELECT id FROM ref_medications WHERE name='Doxybactin'")->fetchColumn();
$db->prepare(
"INSERT INTO treatments(animal_id,medication_id,route,dose_text,start_date,end_date,ongoing,notes,created_by,give_morning,give_evening) VALUES(?,?,'PO','1/2 comprimé matin et soir',date('now','-2 days'),date('now','+5 days'),1,'Traitement fictif de démonstration',?,1,1)",
)->execute([$twix, $med, $userId]);
$db->prepare(
"INSERT INTO medical_notes(animal_id,noted_at,kind,reason,symptoms,exam,diagnosis,plan,weight_kg,temperature_c,created_by,vet_contact_id,clinic_contact_id) VALUES(?,datetime('now','-1 day'),'consult','Visite sanitaire dentrée','Éternuements occasionnels','État général satisfaisant, auscultation sans anomalie','Coryza léger','Surveillance, traitement court et contrôle avant sortie de quarantaine',4.8,38.4,?,?,?)",
)->execute([$twix, $userId, $contactIds['vet'], $contactIds['clinic']]);
$weight = $db->prepare(
"INSERT INTO measurements(animal_id,type,value,unit,measured_at,notes,created_by) VALUES(?,'weight',?,'kg',datetime('now',?),'Mesure fictive',?)",
);
foreach ([[4.6, '-30 days'], [4.7, '-14 days'], [4.8, '-1 day']] as $w) {
$weight->execute([$twix, $w[0], $w[1], $userId]);
}
$db->prepare(
"INSERT OR IGNORE INTO ref_vaccines(name,notes) VALUES('TCL','Vaccin de démonstration')",
)->execute();
$vaccine = (int) $db->query("SELECT id FROM ref_vaccines WHERE name='TCL'")->fetchColumn();
$db->prepare(
"INSERT INTO vaccinations(animal_id,vaccine_id,done_date,due_date,lot,manufacturer,notes,created_by,vet_contact_id,clinic_contact_id) VALUES(?,?,date('now','-11 months'),date('now','+1 month'),'LOT-DEMO-1','Laboratoire Démo','Vaccination fictive à jour',?,?,?)",
)->execute([$twix, $vaccine, $userId, $contactIds['vet'], $contactIds['clinic']]);
$dewormer = (int) $db->query('SELECT id FROM ref_dewormers ORDER BY id LIMIT 1')->fetchColumn();
$db->prepare(
"INSERT INTO dewormings(animal_id,dewormer_id,administered_on,dose_text,weight_kg,next_due_date,notes,created_by) VALUES(?,?,date('now','-10 days'),'1 pipette',4.7,date('now','+80 days'),'Administration fictive',?)",
)->execute([$twix, $dewormer, $userId]);
$db->prepare(
"INSERT INTO animal_health_conditions(animal_id,name,status,diagnosed_at,resolved_at,notes) VALUES(?,'Gingivite légère','resolved',date('now','-6 months'),date('now','-5 months'),'Antécédent fictif résolu')",
)->execute([$twix]);
$db->prepare(
"INSERT INTO lab_reports(animal_id,sampled_on,report_type,laboratory_name,veterinarian_contact_id,clinic_contact_id,notes,created_by) VALUES(?,date('now','-1 day'),'Bilan complet','Laboratoire Démo',?,?,'Prise de sang fictive — valeurs dans les intervalles usuels',?)",
)->execute([$twix, $contactIds['vet'], $contactIds['clinic'], $userId]);
$report = (int) $db->lastInsertId();
$result = $db->prepare(
'INSERT INTO lab_results(report_id,parameter_name,value,unit,reference_min,reference_max,notes,position) VALUES(?,?,?,?,?,?,?,?)',
);
foreach (
[
['Leucocytes', 8.4, '10⁹/L', 3.5, 20.7, 10],
['Érythrocytes', 9.6, '10¹²/L', 7.7, 12.8, 20],
['Hémoglobine', 13.8, 'g/dL', 10, 17, 30],
['Hématocrite', 42.1, '%', 33.7, 55.4, 40],
['Plaquettes', 312, '10⁹/L', 125, 618, 50],
['Créatinine', 12, 'mg/L', 3, 21, 60],
['Urée', 0.42, 'g/L', 0.214, 0.642, 70],
]
as $r
) {
$result->execute([$report, $r[0], $r[1], $r[2], $r[3], $r[4], 'Résultat fictif', $r[5]]);
}
$db->prepare(
"INSERT INTO animal_photos(animal_id,filename,original_name,mime,size_bytes,is_primary,is_public,caption) VALUES(?,'twix.webp','twix.jpg','image/webp',0,1,1,'Twix — photographie de démonstration')",
)->execute([$twix]);
$db->prepare(
"INSERT INTO agenda_items(item_type,title,description,starts_at,ends_at,location,status,priority,animal_id,contact_id,reminder_minutes,created_by) VALUES('veterinary','Contrôle de sortie de quarantaine','Rendez-vous fictif : contrôle clinique et validation de la sortie.',datetime('now','+7 days','09:00'),datetime('now','+7 days','09:30'),'Clinique vétérinaire Démo','planned','high',?,?,60,?)",
)->execute([$twix, $contactIds['clinic'], $userId]);
$db->prepare(
"INSERT INTO animal_placements(animal_id,event_type,event_date,contact_id,reason,notes,created_by) VALUES(?,'foster',date('now','-30 days'),?,'Placement de démonstration','Donnée fictive',?)",
)->execute([$animalIds['Caramel'], $contactIds['fa'], $userId]);
$db->prepare(
"INSERT INTO animal_deaths(animal_id,deceased_date,cause_code,cause_details,occurred_in_care,place_type,euthanized,veterinarian_contact_id,crematorium_contact_id,body_disposition,created_by) VALUES(?,date('now','-60 days'),'old_age','Décès fictif',1,'veterinaire',1,?,?,'collective_cremation',?)",
)->execute([$animalIds['Étoile'], $contactIds['vet'], $contactIds['crematorium'], $userId]);
$history = $db->prepare(
"INSERT INTO animal_history(animal_id,action,field,new_value,user_id,created_at) VALUES(?,'created','demo','Donnée entièrement fictive',?,datetime('now'))",
);
foreach ($animalIds as $id) {
$history->execute([$id, $userId]);
}
return [
'animals' => count($animalIds),
'contacts' => count($contactIds),
'litters' => 1,
'bonded_groups' => 1,
'treatments' => 1,
'vaccinations' => 1,
'lab_reports' => 1,
'agenda_items' => 1,
'photos' => 1,
];
}
public static function clearFiles(): void
{
foreach (
[
dirname(__DIR__, 2) . '/public/media/animals',
dirname(__DIR__, 2) . '/data/grants',
dirname(__DIR__, 2) . '/data/medical-documents',
]
as $root
) {
if (!is_dir($root)) {
continue;
}
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST,
);
foreach ($it as $file) {
$path = $file->getPathname();
if ($file->isLink() || $file->isFile()) {
@unlink($path);
} elseif ($file->isDir()) {
@rmdir($path);
}
}
}
}
public static function installFiles(PDO $db): void
{
$animalId = (int) $db->query("SELECT id FROM animals WHERE internal_code='DEMO-003' LIMIT 1")->fetchColumn();
if ($animalId <= 0) {
return;
}
$source = dirname(__DIR__, 2) . '/resources/demo/twix.webp';
$directory = dirname(__DIR__, 2) . '/public/media/animals/' . $animalId;
$target = $directory . '/twix.webp';
if (!is_file($source)) {
throw new RuntimeException(t('service.image.invalid_upload'));
}
if (!is_dir($directory) && !mkdir($directory, 0755, true) && !is_dir($directory)) {
throw new RuntimeException(t('service.image.directory_failed'));
}
if (!copy($source, $target)) {
throw new RuntimeException(t('service.image.save_failed'));
}
@chmod($target, 0644);
$db->prepare("UPDATE animal_photos SET size_bytes=? WHERE animal_id=? AND filename='twix.webp'")->execute([
filesize($target),
$animalId,
]);
}
}