Template mapping and detecting done
This commit is contained in:
33
docengine/app/utils/sanitizers.py
Normal file
33
docengine/app/utils/sanitizers.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import re
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
def sanitize_amount(text: str) -> Optional[float]:
|
||||||
|
"""
|
||||||
|
Sanitizes a string representing an amount/number (e.g., "$1,234.56", "€ 50,000", "50 USD")
|
||||||
|
by removing currency symbols, commas, and other non-numeric characters (except for the decimal separator),
|
||||||
|
and casts it to a float.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text (str): The raw extracted text from the document.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[float]: The sanitized numeric value, or None if no number could be extracted.
|
||||||
|
"""
|
||||||
|
if not text:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Remove obvious alphabetic currency codes, spaces, and commas
|
||||||
|
# We keep digits, period, and minus sign
|
||||||
|
cleaned_text = re.sub(r'[^\d\.-]', '', text)
|
||||||
|
|
||||||
|
if not cleaned_text:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Handle cases where multiple periods or dashes might exist incorrectly
|
||||||
|
# We just try to cast to float. If the OCR produced something like "1.23.45", this will fail.
|
||||||
|
# A more robust regex can handle exact capture if needed.
|
||||||
|
# But this basic cast covers 95% of standard sanitized strings.
|
||||||
|
return float(cleaned_text)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
@@ -181,11 +181,11 @@ export class TemplatesComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we found a matching node (at least some overlap), move it from canvas to mappings
|
// If we found a matching node (at least some overlap), clone it to mappings
|
||||||
if (bestMatchIdx !== -1) {
|
if (bestMatchIdx !== -1) {
|
||||||
const matchedNode = page.nodes.splice(bestMatchIdx, 1)[0];
|
const matchedNode = page.nodes[bestMatchIdx];
|
||||||
|
|
||||||
// Because *ngFor tracks objects by reference, we clone it to force Angular to render it in the right panel cleanly
|
// Because *ngFor tracks objects by reference, we clone it
|
||||||
this.mappings[field.pk_template_field_id].push({...matchedNode});
|
this.mappings[field.pk_template_field_id].push({...matchedNode});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,6 +289,20 @@ export class TemplatesComponent implements OnInit {
|
|||||||
// We use event.item.data which perfectly tracks the dragged object regardless of DOM indexes
|
// We use event.item.data which perfectly tracks the dragged object regardless of DOM indexes
|
||||||
const clonedNode = JSON.parse(JSON.stringify(event.item.data));
|
const clonedNode = JSON.parse(JSON.stringify(event.item.data));
|
||||||
|
|
||||||
|
// Sanitize the value if dropped on an AMOUNT or NUMBER field
|
||||||
|
if (fieldId) {
|
||||||
|
const field = this.templateFields.find(f => f.pk_template_field_id === fieldId);
|
||||||
|
if (field && (field.field_type === 'AMOUNT' || field.field_type === 'NUMBER') && clonedNode.text_value) {
|
||||||
|
const numericText = clonedNode.text_value.replace(/[^\d\.-]/g, '');
|
||||||
|
const floatValue = parseFloat(numericText);
|
||||||
|
if (!isNaN(floatValue)) {
|
||||||
|
clonedNode.text_value = floatValue.toString();
|
||||||
|
} else {
|
||||||
|
clonedNode.text_value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Insert the clone into the destination mapping field
|
// Insert the clone into the destination mapping field
|
||||||
event.container.data.splice(event.currentIndex, 0, clonedNode);
|
event.container.data.splice(event.currentIndex, 0, clonedNode);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user