OHO-16(Perfect Regex)

This commit is contained in:
lokeshramchand-ctrl 2025-08-22 10:47:16 +05:30
parent fd80a9be70
commit 030c8bd99d
2 changed files with 97 additions and 47 deletions

View file

@ -227,7 +227,7 @@ app.post('/api/transactions/voice', async (req, res) => {
.trim(); .trim();
const description = cleaned || 'misc'; const description = cleaned || 'misc';
// Step 3: Predict category using your Flask API // Step 3: Predict category using your Flask API
const predictRes = await axios.post('http://10.231.41.181:5000/api/predict', { const predictRes = await axios.post('http://192.168.1.101:5000/api/predict', {
description, description,
}); });
const category = predictRes.data?.category || 'Other'; const category = predictRes.data?.category || 'Other';
@ -256,51 +256,86 @@ const bankRules = [
]; ];
// ---------- PARSER ---------- // ---------- PARSER ----------
function parseBankMessage(snippet) { function parseBankMessage(email) {
const result = { const result = {
amount: null, amount: null,
vendor: null, vendor: null,
rawVendor: null,
type: null, type: null,
date: null, date: null,
referenceNumber: null, referenceNumber: null,
}; };
// Amount // ---------- Amount ----------
const amtMatch = snippet.match(/(?:Rs\.?|INR)\s*([\d,]+\.?\d*)/i); const amtMatch = email.match(/(?:Rs\.?|INR)\s*([\d,]+\.?\d*)/i);
if (amtMatch) result.amount = parseFloat(amtMatch[1].replace(/,/g, "")); if (amtMatch) result.amount = parseFloat(amtMatch[1].replace(/,/g, ""));
// Debit / Credit // ---------- Debit / Credit ----------
if (/debited/i.test(snippet)) result.type = "debit"; if (/debited/i.test(email)) result.type = "debit";
else if (/credited/i.test(snippet)) result.type = "credit"; else if (/credited/i.test(email)) result.type = "credit";
// Date // ---------- Date ----------
const dateMatch = snippet.match(/on\s+(\d{2}[-/]\d{2}[-/]\d{2,4})/i); const dateMatch = email.match(/on\s+(\d{2}[-/]\d{2}[-/]\d{2,4})/i);
if (dateMatch) result.date = dateMatch[1]; if (dateMatch) result.date = dateMatch[1];
// Reference Number (flexible regex) // ---------- Reference Number ----------
const refMatch = snippet.match(/(?:reference number is|UPI reference number is|Ref No:?)\s*([A-Za-z0-9]+)/i); const refMatch = email.match(/(?:reference number is|UPI transaction reference number is|Ref No:?)\s*([A-Za-z0-9]+)/i);
if (refMatch) result.referenceNumber = refMatch[1].trim(); if (refMatch) result.referenceNumber = refMatch[1].trim();
// Vendor extraction - remove emails, pick last uppercase phrase // ---------- Vendor Extraction ----------
let vendorCandidate = snippet.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, '').trim(); let vendorCandidate = null;
const vendorWords = vendorCandidate.match(/(?:\b[A-Z]{2,}\b(?:\s)?)+/g); // 1. Prefer "to VPA ..." pattern, stop at 'on|your|Ref|Txn' etc.
if (vendorWords && vendorWords.length > 0) { const toMatch = email.match(/to\s+(?:VPA\s+)?(?:[^\s@]+\@[^\s]+\s+)?([A-Za-z0-9\s&.-]+)/i);
result.vendor = vendorWords[vendorWords.length - 1].trim(); if (toMatch) {
} else { vendorCandidate = toMatch[1]
// Fallback: extract after "to " or "at " .split(/ on | your | ref | txn | transaction | number /i)[0] // cut off junk
const toMatch = snippet.match(/to\s+([A-Za-z0-9\s&.-]+)/i); .trim();
if (toMatch) { }
let v = toMatch[1].trim();
v = v.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, '').trim(); // 2. Fallback: fully uppercase phrases
v = v.replace(/\s{2,}/g, ' '); if (!vendorCandidate) {
result.vendor = v; const upperMatch = email.match(/(?:\b[A-Z][A-Z0-9&.-]+\b(?:\s)?)+/g);
if (upperMatch) {
vendorCandidate = upperMatch[upperMatch.length - 1].trim();
} }
} }
// Assign rawVendor
if (vendorCandidate) {
result.rawVendor = vendorCandidate;
}
// ---------- Cleaning Pipeline ----------
if (result.rawVendor) {
let v = result.rawVendor;
// Remove noise keywords
v = v.replace(/\b(?:UPI|VPA|REF|Txn|Order|Payment|NEFT|IMPS|Transaction|Number|Your)\b/gi, "");
// Remove emails
v = v.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, "");
// Remove dates & numbers that sneak in
v = v.replace(/\d{2}[-/]\d{2}[-/]\d{2,4}/g, "");
v = v.replace(/\b\d{6,}\b/g, ""); // strip long numbers like reference ids
// Replace underscores/dashes with space
v = v.replace(/[_\-]+/g, " ");
// Collapse multiple spaces
v = v.replace(/\s{2,}/g, " ").trim();
// Normalize casing
v = v.toLowerCase().replace(/\b\w/g, (c) => c.toUpperCase());
result.vendor = v;
}
return result; return result;
} }
// ---------- FETCH EMAILS ---------- // ---------- FETCH EMAILS ----------
async function fetchBankEmails(accessToken, bankEmails) { async function fetchBankEmails(accessToken, bankEmails) {
const oauth2Client = new google.auth.OAuth2(); const oauth2Client = new google.auth.OAuth2();

View file

@ -12,54 +12,68 @@ function parseBankMessage(email) {
referenceNumber: null, referenceNumber: null,
}; };
// Amount // ---------- Amount ----------
const amtMatch = email.match(/(?:Rs\.?|INR)\s*([\d,]+\.?\d*)/i); const amtMatch = email.match(/(?:Rs\.?|INR)\s*([\d,]+\.?\d*)/i);
if (amtMatch) result.amount = parseFloat(amtMatch[1].replace(/,/g, "")); if (amtMatch) result.amount = parseFloat(amtMatch[1].replace(/,/g, ""));
// Debit / Credit // ---------- Debit / Credit ----------
if (/debited/i.test(email)) result.type = "debit"; if (/debited/i.test(email)) result.type = "debit";
else if (/credited/i.test(email)) result.type = "credit"; else if (/credited/i.test(email)) result.type = "credit";
// Date // ---------- Date ----------
const dateMatch = email.match(/on\s+(\d{2}[-/]\d{2}[-/]\d{2,4})/i); const dateMatch = email.match(/on\s+(\d{2}[-/]\d{2}[-/]\d{2,4})/i);
if (dateMatch) result.date = dateMatch[1]; if (dateMatch) result.date = dateMatch[1];
// Reference Number (flexible regex) // ---------- Reference Number ----------
const refMatch = email.match(/(?:reference number is|UPI reference number is|Ref No:?)\s*([A-Za-z0-9]+)/i); const refMatch = email.match(/(?:reference number is|UPI transaction reference number is|Ref No:?)\s*([A-Za-z0-9]+)/i);
if (refMatch) result.referenceNumber = refMatch[1].trim(); if (refMatch) result.referenceNumber = refMatch[1].trim();
// Vendor extraction - remove emails, pick last uppercase phrase // ---------- Vendor Extraction ----------
let vendorCandidate = email.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, '').trim(); let vendorCandidate = null;
const vendorWords = vendorCandidate.match(/(?:\b[A-Z]{2,}\b(?:\s)?)+/g); // 1. Prefer "to VPA ..." pattern, stop at 'on|your|Ref|Txn' etc.
if (vendorWords && vendorWords.length > 0) { const toMatch = email.match(/to\s+(?:VPA\s+)?(?:[^\s@]+\@[^\s]+\s+)?([A-Za-z0-9\s&.-]+)/i);
result.rawVendor = vendorWords[vendorWords.length - 1].trim(); // store raw if (toMatch) {
} else { vendorCandidate = toMatch[1]
// Improved vendor fallback .split(/ on | your | ref | txn | transaction | number /i)[0] // cut off junk
const toMatch = email.match(/to\s+(?:VPA\s+)?(?:[^\s@]+\@[^\s]+\s+)?([A-Za-z0-9\s&.-]+)/i); .trim();
if (toMatch) { }
let v = toMatch[1].trim();
v = v.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, '').trim(); // remove emails // 2. Fallback: fully uppercase phrases
v = v.replace(/\s{2,}/g, ' '); if (!vendorCandidate) {
result.rawVendor = v; const upperMatch = email.match(/(?:\b[A-Z][A-Z0-9&.-]+\b(?:\s)?)+/g);
if (upperMatch) {
vendorCandidate = upperMatch[upperMatch.length - 1].trim();
} }
} }
// Assign rawVendor
if (vendorCandidate) {
result.rawVendor = vendorCandidate;
}
// ---------- Cleaning Pipeline ---------- // ---------- Cleaning Pipeline ----------
if (result.rawVendor) { if (result.rawVendor) {
let v = result.rawVendor; let v = result.rawVendor;
// Remove noise keywords // Remove noise keywords
v = v.replace(/\b(?:UPI|VPA|REF|Txn|Order|Payment|NEFT|IMPS)\b/gi, ''); v = v.replace(/\b(?:UPI|VPA|REF|Txn|Order|Payment|NEFT|IMPS|Transaction|Number|Your)\b/gi, "");
// Remove emails
v = v.replace(/[a-z0-9._%+-]+@[a-z0-9.-]+/gi, "");
// Remove dates & numbers that sneak in
v = v.replace(/\d{2}[-/]\d{2}[-/]\d{2,4}/g, "");
v = v.replace(/\b\d{6,}\b/g, ""); // strip long numbers like reference ids
// Replace underscores/dashes with space // Replace underscores/dashes with space
v = v.replace(/[_\-]+/g, ' '); v = v.replace(/[_\-]+/g, " ");
// Collapse multiple spaces // Collapse multiple spaces
v = v.replace(/\s{2,}/g, ' ').trim(); v = v.replace(/\s{2,}/g, " ").trim();
// Normalize casing (title case) // Normalize casing
v = v.toLowerCase().replace(/\b\w/g, c => c.toUpperCase()); v = v.toLowerCase().replace(/\b\w/g, (c) => c.toUpperCase());
result.vendor = v; result.vendor = v;
} }
@ -67,6 +81,7 @@ function parseBankMessage(email) {
return result; return result;
} }
console.log("=== Bank Email Regex Test ==="); console.log("=== Bank Email Regex Test ===");
testEmails.forEach((email, idx) => { testEmails.forEach((email, idx) => {
console.log(`\n-- Email ${idx + 1} --`); console.log(`\n-- Email ${idx + 1} --`);