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,169 @@
<?php
declare(strict_types=1);
final class Asm3ClinicalImporter
{
public static function run(string $path, PDO $db, bool $apply = false, bool $createBackup = true): array
{
$hash = hash_file('sha256', $path);
$src = Asm3Analyzer::rows($path, ['animalvaccination', 'vaccinationtype', 'log', 'logtype']);
$links = self::animalLinks($db, $hash);
$types = [];
foreach ($src['vaccinationtype'] ?? [] as $r) {
$types[(int) $r['ID']] = trim((string) $r['VACCINATIONTYPE']);
}
$logTypes = [];
foreach ($src['logtype'] ?? [] as $r) {
$logTypes[(int) $r['ID']] = trim((string) $r['LOGTYPENAME']);
}
$result = [
'mode' => $apply ? 'apply' : 'dry-run',
'read_only' => !$apply,
'vaccinations' => 0,
'medical_notes' => 0,
'weights' => 0,
'unresolved' => 0,
'already_imported' => 0,
'backup' => null,
];
foreach ($src['animalvaccination'] ?? [] as $r) {
$result['vaccinations']++;
if (!isset($links[(int) $r['ANIMALID']])) {
$result['unresolved']++;
}
}
foreach ($src['log'] ?? [] as $r) {
if ((int) ($r['LINKTYPE'] ?? -1) === 0) {
if (!isset($links[(int) $r['LINKID']])) {
$result['unresolved']++;
}
$name = mb_strtolower($logTypes[(int) $r['LOGTYPEID']] ?? '');
if ($name === 'poids') {
$result['weights']++;
} else {
$result['medical_notes']++;
}
}
}
if (!$apply) {
return $result;
}
if ($createBackup) {
$b = BackupService::create('pre-asm3-import');
$result['backup'] = $b['name'] ?? null;
}
$ownsTransaction = !$db->inTransaction();
if ($ownsTransaction) {
$db->beginTransaction();
}
try {
$runId = self::runId($db, $path, $hash, $result['backup'], 'médical');
foreach ($src['animalvaccination'] ?? [] as $r) {
$animal = $links[(int) $r['ANIMALID']] ?? 0;
if (!$animal) {
continue;
}
$source = (int) $r['ID'];
if (self::seen($db, $hash, 'vaccination', $source)) {
$result['already_imported']++;
continue;
}
$name = $types[(int) $r['VACCINATIONID']] ?? 'Vaccin ASM3 ' . (int) $r['VACCINATIONID'];
$db->prepare('INSERT OR IGNORE INTO ref_vaccines(name) VALUES(:name)')->execute([':name' => $name]);
$q = $db->prepare('SELECT id FROM ref_vaccines WHERE name=:name');
$q->execute([':name' => $name]);
$vaccine = (int) $q->fetchColumn();
$db->prepare(
'INSERT INTO vaccinations(animal_id,vaccine_id,done_date,due_date,lot,notes,manufacturer,batch_expires_on,administered_by_name) VALUES(:animal,:vaccine,:done,:due,:lot,:notes,:manufacturer,:expires,:by)',
)->execute([
':animal' => $animal,
':vaccine' => $vaccine,
':done' =>
self::date($r['DATEOFVACCINATION'] ?? null) ?:
self::date($r['DATEREQUIRED'] ?? null) ?:
date('Y-m-d'),
':due' => self::date($r['DATEREQUIRED'] ?? null),
':lot' => trim((string) $r['BATCHNUMBER']) ?: null,
':notes' => trim((string) $r['COMMENTS']) ?: null,
':manufacturer' => trim((string) $r['MANUFACTURER']) ?: null,
':expires' => self::date($r['BATCHEXPIRYDATE'] ?? null),
':by' => trim((string) $r['GIVENBY']) ?: null,
]);
self::link($db, $hash, 'vaccination', $source, (int) $db->lastInsertId(), $runId);
}
foreach ($src['log'] ?? [] as $r) {
if ((int) ($r['LINKTYPE'] ?? -1) !== 0) {
continue;
}
$animal = $links[(int) $r['LINKID']] ?? 0;
if (!$animal) {
continue;
}
$source = (int) $r['ID'];
$name = $logTypes[(int) $r['LOGTYPEID']] ?? 'Historique';
$entity = mb_strtolower($name) === 'poids' ? 'measurement' : 'medical-log';
if (self::seen($db, $hash, $entity, $source)) {
continue;
}
$text = trim((string) $r['COMMENTS']);
$date = self::date($r['DATE'] ?? null) ?: date('Y-m-d');
if ($entity === 'measurement' && preg_match('/([0-9]+(?:[.,][0-9]+)?)/', $text, $m)) {
$value = (float) str_replace(',', '.', $m[1]);
$db->prepare(
"INSERT INTO measurements(animal_id,measured_at,type,value,unit,notes) VALUES(:animal,:date,'weight',:value,'kg',:notes)",
)->execute([':animal' => $animal, ':date' => $date, ':value' => $value, ':notes' => $text]);
} else {
$db->prepare(
"INSERT INTO medical_notes(animal_id,noted_at,kind,reason,plan) VALUES(:animal,:date,'autre',:reason,:plan)",
)->execute([':animal' => $animal, ':date' => $date, ':reason' => $name, ':plan' => $text ?: null]);
}
self::link($db, $hash, $entity, $source, (int) $db->lastInsertId(), $runId);
}
if ($ownsTransaction) {
$db->commit();
}
} catch (Throwable $e) {
if ($ownsTransaction && $db->inTransaction()) {
$db->rollBack();
}
throw $e;
}
return $result;
}
private static function animalLinks(PDO $db, string $hash): array
{
$s = $db->prepare(
"SELECT source_id,target_id FROM asm3_import_links WHERE source_sha256=:h AND entity_type='animal'",
);
$s->execute([':h' => $hash]);
$o = [];
foreach ($s as $r) {
$o[(int) $r['source_id']] = (int) $r['target_id'];
}
return $o;
}
private static function runId(PDO $db, string $p, string $h, ?string $b, string $label): int
{
$db->prepare(
"INSERT INTO asm3_import_runs(source_name,source_sha256,status,summary_json,backup_name) VALUES(:n,:h,'completed','{}',:b)",
)->execute([':n' => basename($p) . ' · ' . $label, ':h' => $h, ':b' => $b]);
return (int) $db->lastInsertId();
}
private static function seen(PDO $db, string $h, string $e, int $s): bool
{
$q = $db->prepare('SELECT 1 FROM asm3_import_links WHERE source_sha256=:h AND entity_type=:e AND source_id=:s');
$q->execute([':h' => $h, ':e' => $e, ':s' => $s]);
return (bool) $q->fetchColumn();
}
private static function link(PDO $db, string $h, string $e, int $s, int $t, int $r): void
{
$db->prepare(
'INSERT INTO asm3_import_links(source_sha256,entity_type,source_id,target_id,import_run_id) VALUES(:h,:e,:s,:t,:r)',
)->execute([':h' => $h, ':e' => $e, ':s' => $s, ':t' => $t, ':r' => $r]);
}
private static function date(mixed $v): ?string
{
return $v && preg_match('/^\d{4}-\d{2}-\d{2}/', (string) $v, $m) ? $m[0] : null;
}
}