edtech / apps /api /src /services /normalization.ts
CognxSafeTrack
refactor(debt): resolve all 10 technical debt items from audit
a966957
import { prisma } from './prisma';
import { logger } from '../logger';
import { redis } from '../lib/redis';
const CACHE_KEY = 'normalization:rules:wolof';
const CACHE_TTL = 3600; // 1 hour
export const normalizationService = {
/**
* Get all normalization rules for a language
*/
async getRules(language: string = 'WOLOF'): Promise<Record<string, string>> {
const cacheKey = `${CACHE_KEY}:${language}`;
try {
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
} catch (err) {
logger.error({ err }, '[NORMALIZATION] Redis get error');
}
const rules = await prisma.normalizationRule.findMany({
where: { language }
});
const ruleMap: Record<string, string> = {};
rules.forEach((r: { original: string; replacement: string }) => {
ruleMap[r.original.toLowerCase()] = r.replacement;
});
try {
await redis.set(cacheKey, JSON.stringify(ruleMap), 'EX', CACHE_TTL);
} catch (err) {
logger.error({ err }, '[NORMALIZATION] Redis set error');
}
return ruleMap;
},
/**
* Create or update a rule
*/
async saveRule(original: string, replacement: string, language: string = 'WOLOF') {
const rule = await prisma.normalizationRule.upsert({
where: { original },
update: { replacement, language },
create: { original, replacement, language }
});
// Invalidate cache
await redis.del(`${CACHE_KEY}:${language}`).catch(() => {});
return rule;
},
/**
* Batch save rules
*/
async saveRules(rules: { original: string, replacement: string }[], language: string = 'WOLOF') {
const operations = rules.map(r => prisma.normalizationRule.upsert({
where: { original: r.original },
update: { replacement: r.replacement, language },
create: { original: r.original, replacement: r.replacement, language }
}));
await Promise.all(operations);
await redis.del(`${CACHE_KEY}:${language}`).catch(() => {});
}
};