fix(food): affectation plat/garniture par plus proche voisin vertical
Deploy HouseHub / deploy (push) Successful in 1s

La fenêtre à tolérance fixe (+/-20) pour distinguer Plat Chaud et
Garniture ratait des lignes selon le nombre de lignes du plat (le
débordement du texte au-dessus de son label varie d'une semaine à
l'autre). Remplacé par une affectation "rangée la plus proche" pour
chaque texte, sur le même principe déjà utilisé pour les colonnes de
jour : robuste quelle que soit la hauteur du bloc.

Corrige aussi la perte du caractère "œ" (ex: "bœuf" -> "buf") dans le
nettoyage du texte, la plage Unicode À-ÿ ne le couvrant pas.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
percoandClaude Sonnet 5 committed 2026-09-25 14:57:30 +02:00
1 parent 3a64934eb5
commit bfa70402a9
1 file changed
+17 -27
+17 -27
View File
@@ -87,25 +87,7 @@ class NativePdfParser {
}
}
// Tri de haut en bas (Y du PDF est décroissant du haut vers le bas)
usort($rowHeaders, fn($a, $b) => $b['y'] <=> $a['y']);
$yPlatTop = null; $yPlatBot = null;
$yGarTop = null; $yGarBot = null;
for ($i = 0; $i < count($rowHeaders); $i++) {
if ($rowHeaders[$i]['label'] === 'PLATCHAUD') {
$yPlatTop = $rowHeaders[$i]['y'];
// La limite basse est le label suivant, ou -50 unités
$yPlatBot = $rowHeaders[$i+1]['y'] ?? ($yPlatTop - 50);
}
if ($rowHeaders[$i]['label'] === 'GARNITURE') {
$yGarTop = $rowHeaders[$i]['y'];
$yGarBot = $rowHeaders[$i+1]['y'] ?? ($yGarTop - 50);
}
}
if (!$yPlatTop || !$yGarTop) {
if (!in_array('PLATCHAUD', array_column($rowHeaders, 'label')) || !in_array('GARNITURE', array_column($rowHeaders, 'label'))) {
throw new Exception("Structure du menu non reconnue (Mots-clés 'PLAT CHAUD' et 'GARNITURE' introuvables).");
}
@@ -120,18 +102,26 @@ class NativePdfParser {
// Retrait des labels BIO/HVE et mentions superflues
$textVal = trim(preg_replace('/\b(AB|HVE|MSC|Label Rouge)\b/i', '', $t['text']));
$textVal = str_replace(['(plat', 'complet)'], '', $textVal);
$textVal = trim(preg_replace('/[^a-zA-ZÀ-ÿ0-9\s&\-]/u', '', $textVal));
$textVal = trim(preg_replace('/[^a-zA-ZÀ-ÿŒœÆæ0-9\s&\-]/u', '', $textVal));
if (strlen($textVal) < 3) continue;
// Filtre Vertical : Est-on dans la tranche Plat Chaud ou Garniture ? (Tolérance +/- 5 unités)
// Filtre Vertical : de quelle rangée (en-tête) ce texte est-il le plus proche ?
// Le texte d'une rangée déborde souvent au-dessus de son propre label (lignes
// empilées vers le haut), y compris pour le label de la rangée suivante qui
// sert de limite basse : on applique donc la même marge des deux côtés.
$inPlat = ($t['y'] <= $yPlatTop + 20 && $t['y'] >= $yPlatBot + 20);
$inGar = ($t['y'] <= $yGarTop + 20 && $t['y'] >= $yGarBot + 20);
// empilées vers le haut) sur une hauteur variable d'un menu à l'autre : une
// fenêtre à tolérance fixe est donc peu fiable. Le plus proche voisin (même
// principe que pour les colonnes de jour ci-dessous) s'adapte automatiquement.
$bestRow = null;
$minRowDiff = PHP_FLOAT_MAX;
foreach ($rowHeaders as $rh) {
$diff = abs($t['y'] - $rh['y']);
if ($diff < $minRowDiff) {
$minRowDiff = $diff;
$bestRow = $rh['label'];
}
}
if ($inPlat || $inGar) {
if ($bestRow === 'PLATCHAUD' || $bestRow === 'GARNITURE') {
// Filtre Horizontal : Quel est le jour le plus proche ?
$bestCol = 0;
$minDiff = PHP_FLOAT_MAX;
@@ -142,7 +132,7 @@ class NativePdfParser {
$bestCol = $idx;
}
}
$days[$bestCol][] = ['text' => $textVal, 'y' => $t['y'], 'type' => $inPlat ? 'plat' : 'gar'];
$days[$bestCol][] = ['text' => $textVal, 'y' => $t['y'], 'type' => $bestRow === 'PLATCHAUD' ? 'plat' : 'gar'];
}
}