BillTracker/services/advisoryFilterService.js

82 lines
1.9 KiB
JavaScript
Raw Permalink Normal View History

'use strict';
const { getDb } = require('../db/database');
// Lazy-loaded in-memory cache — loaded once on first use
let _patterns = null;
let _overrideTerms = null;
function normalize(text) {
if (!text) return '';
return String(text)
.toLowerCase()
.replace(/&/g, 'and')
.replace(/\s+/g, ' ')
.trim();
}
function loadCache() {
if (_patterns !== null) return;
const db = getDb();
_patterns = db.prepare(
'SELECT pattern, confidence, category, rationale FROM advisory_non_bill_filters'
).all();
_overrideTerms = db.prepare(
'SELECT term FROM advisory_bill_like_overrides'
).all().map(r => r.term);
}
/**
* Check a transaction title against advisory filter patterns.
* Returns null if Create Bill should be shown normally, or
* { confidence: 'high'|'medium', category, rationale } if it should be suppressed.
*/
function checkTransaction(title) {
if (!title) return null;
try {
loadCache();
} catch {
return null;
}
const normalized = normalize(title);
if (!normalized) return null;
// Bill-like override terms take priority — always show Create Bill
for (const term of _overrideTerms) {
if (normalized.includes(term)) return null;
}
// Find the highest-confidence matching pattern
let highMatch = null;
let mediumMatch = null;
for (const row of _patterns) {
if (normalized.includes(row.pattern)) {
if (row.confidence === 'high') {
highMatch = row;
break; // high confidence — no need to keep looking
} else if (!mediumMatch) {
mediumMatch = row;
}
}
}
const match = highMatch || mediumMatch;
if (!match) return null;
return {
confidence: match.confidence,
category: match.category,
rationale: match.rationale || null,
};
}
/** Clear the in-memory cache (used after re-seeding in tests). */
function clearCache() {
_patterns = null;
_overrideTerms = null;
}
module.exports = { checkTransaction, clearCache };