fix(ingest): resolve Docling stacked fraction OCR artifacts into clean measurements
This commit is contained in:
@@ -222,13 +222,23 @@ function parseMetricAmount(str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function cleanFractionText(str) {
|
||||
if (!str) return "";
|
||||
return str
|
||||
.replace(/[\r\n]+/g, " ")
|
||||
.replace(/(\d+)\s*\/\s*\1\s*\/\s*(\d+)/g, (m, a, b) => `${a}/${b}`)
|
||||
.replace(/(\d+)\s*\/\s*(\d+)/g, (m, a, b) => `${a}/${b}`)
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function parseUsFallback(text, name) {
|
||||
if (!text) {
|
||||
if (/zest/i.test(name)) return { quantity: 1, unit_id: "each", notes: "Zest of 1" };
|
||||
if (/vanilla bean/i.test(name)) return { quantity: 1, unit_id: "each", notes: "1 bean" };
|
||||
return { quantity: 1, unit_id: "gram", notes: "To taste / as needed" };
|
||||
}
|
||||
const s = text.replace(/[\r\n\s]+/g, " ").trim().toLowerCase();
|
||||
const s = cleanFractionText(text).toLowerCase();
|
||||
if (s.includes("1/8") || s.includes("⅛")) return { quantity: 0.6, unit_id: "gram", notes: "⅛ tsp" };
|
||||
if (s.includes("1/4") || s.includes("¼")) return { quantity: 1.25, unit_id: "gram", notes: "¼ tsp" };
|
||||
if (s.includes("1/2") || s.includes("½")) return { quantity: 2.5, unit_id: "gram", notes: "½ tsp" };
|
||||
@@ -244,23 +254,23 @@ function parseUsFallback(text, name) {
|
||||
|
||||
const ozMatch = s.match(/^([\d.]+)\s*oz$/);
|
||||
if (ozMatch && parseFloat(ozMatch[1]) > 0) {
|
||||
return { quantity: Math.round(parseFloat(ozMatch[1]) * 28.3495 * 100) / 100, unit_id: "gram", notes: text };
|
||||
return { quantity: Math.round(parseFloat(ozMatch[1]) * 28.3495 * 100) / 100, unit_id: "gram", notes: cleanFractionText(text) };
|
||||
}
|
||||
|
||||
return { quantity: 1, unit_id: "gram", notes: text };
|
||||
return { quantity: 1, unit_id: "gram", notes: cleanFractionText(text) };
|
||||
}
|
||||
|
||||
function cleanIngredientName(raw) {
|
||||
let name = raw.trim().replace(/^[\s•\-\*]+/, "");
|
||||
let cleaned = cleanFractionText(raw).replace(/^[\s•\-\*]+/, "");
|
||||
let notes = null;
|
||||
|
||||
const parenMatch = name.match(/^([^(]+)\s*\(([^)]+)\)$/);
|
||||
const parenMatch = cleaned.match(/^([^(]+)\s*\(([^)]+)\)$/);
|
||||
if (parenMatch) {
|
||||
name = parenMatch[1].trim();
|
||||
cleaned = parenMatch[1].trim();
|
||||
notes = parenMatch[2].trim();
|
||||
}
|
||||
|
||||
return { name, notes };
|
||||
return { name: cleaned, notes };
|
||||
}
|
||||
|
||||
function inferIngredientId(name) {
|
||||
@@ -397,9 +407,9 @@ export function parseDoclingBook(doclingJson, targetPages = null) {
|
||||
const row = grid.get(r);
|
||||
if (!row) continue;
|
||||
|
||||
const ingText = row.get(0) || "";
|
||||
const metricText = row.get(1) || "";
|
||||
const usText = row.get(2) || "";
|
||||
const ingText = cleanFractionText(row.get(0) || "");
|
||||
const metricText = cleanFractionText(row.get(1) || "");
|
||||
const usText = cleanFractionText(row.get(2) || "");
|
||||
|
||||
if (/total weight/i.test(ingText)) {
|
||||
const parsedTotal = parseMetricAmount(metricText) || parseMetricAmount(usText);
|
||||
|
||||
Reference in New Issue
Block a user