Checkpoint: reclassifyWithAI traite désormais NULL + rules, supprime les non pertinents et conserve les tombstones
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"version": "2bfe4358",
|
||||
"timestamp": 1783252262810
|
||||
"version": "aa6b358c",
|
||||
"timestamp": 1783341062192
|
||||
}
|
||||
@@ -37,7 +37,7 @@ import { scheduleDailyImport } from "./_core/index";
|
||||
import { loginLocalUser, hashPassword, ensureAdminExists } from "./localAuth";
|
||||
import { classifyArticle } from "./aiClassifier";
|
||||
import { getDb } from "./db";
|
||||
import { veilleItems, aapItems, articleReads } from "../drizzle/schema";
|
||||
import { veilleItems, aapItems, articleReads, processedDedupKeys } from "../drizzle/schema";
|
||||
import { isNull, or, eq as eqDrizzle, and, inArray, count } from "drizzle-orm";
|
||||
|
||||
// ─── Middleware admin ─────────────────────────────────────────────────────────
|
||||
@@ -116,14 +116,15 @@ export const appRouter = router({
|
||||
const db = await getDb();
|
||||
if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "DB indisponible" });
|
||||
|
||||
// Récupérer les articles non encore classés par l'IA
|
||||
// Récupérer les articles non encore classés par l'IA OU classés par le fallback rules
|
||||
const rows = await db
|
||||
.select()
|
||||
.from(veilleItems)
|
||||
.where(or(isNull(veilleItems.iaClassifiedBy), isNull(veilleItems.iaRelevant)))
|
||||
.where(or(isNull(veilleItems.iaClassifiedBy), isNull(veilleItems.iaRelevant), eqDrizzle(veilleItems.iaClassifiedBy, "rules")))
|
||||
.limit(input.limit);
|
||||
|
||||
let processed = 0;
|
||||
let deleted = 0;
|
||||
let errors = 0;
|
||||
|
||||
for (const row of rows) {
|
||||
@@ -133,6 +134,17 @@ export const appRouter = router({
|
||||
row.resume || "",
|
||||
() => ({ typeVeille: (row.typeVeille || "informationnelle") as "reglementaire" | "concurrentielle" | "technologique" | "informationnelle" })
|
||||
);
|
||||
if (!aiResult.relevant) {
|
||||
// Supprimer l'article non pertinent
|
||||
await db.delete(veilleItems).where(eqDrizzle(veilleItems.id, row.id));
|
||||
// Conserver le tombstone pour éviter la réinsertion
|
||||
if (row.dedupKey) {
|
||||
await db.insert(processedDedupKeys)
|
||||
.values({ dedupKey: row.dedupKey, feedType: "veille" })
|
||||
.onDuplicateKeyUpdate({ set: { dedupKey: row.dedupKey } });
|
||||
}
|
||||
deleted++;
|
||||
} else {
|
||||
await db
|
||||
.update(veilleItems)
|
||||
.set({
|
||||
@@ -143,12 +155,13 @@ export const appRouter = router({
|
||||
})
|
||||
.where(eqDrizzle(veilleItems.id, row.id));
|
||||
processed++;
|
||||
}
|
||||
} catch {
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
return { processed, errors, total: rows.length };
|
||||
return { processed, deleted, errors, total: rows.length };
|
||||
}),
|
||||
|
||||
// ─── Marquage lu/non lu ──────────────────────────────────────────────────────────────────────
|
||||
@@ -240,19 +253,29 @@ export const appRouter = router({
|
||||
const rows = await db
|
||||
.select()
|
||||
.from(aapItems)
|
||||
.where(or(isNull(aapItems.iaClassifiedBy), isNull(aapItems.iaRelevant)))
|
||||
.where(or(isNull(aapItems.iaClassifiedBy), isNull(aapItems.iaRelevant), eqDrizzle(aapItems.iaClassifiedBy, "rules")))
|
||||
.limit(input.limit);
|
||||
|
||||
let processed = 0;
|
||||
let deleted = 0;
|
||||
let errors = 0;
|
||||
|
||||
for (const row of rows) {
|
||||
try {
|
||||
const aiResult = await classifyArticle(
|
||||
row.titre || "",
|
||||
"",
|
||||
row.resume || "",
|
||||
() => ({ typeVeille: "informationnelle" as const })
|
||||
);
|
||||
if (!aiResult.relevant) {
|
||||
await db.delete(aapItems).where(eqDrizzle(aapItems.id, row.id));
|
||||
if (row.dedupKey) {
|
||||
await db.insert(processedDedupKeys)
|
||||
.values({ dedupKey: row.dedupKey, feedType: "aap" })
|
||||
.onDuplicateKeyUpdate({ set: { dedupKey: row.dedupKey } });
|
||||
}
|
||||
deleted++;
|
||||
} else {
|
||||
await db
|
||||
.update(aapItems)
|
||||
.set({
|
||||
@@ -262,12 +285,13 @@ export const appRouter = router({
|
||||
})
|
||||
.where(eqDrizzle(aapItems.id, row.id));
|
||||
processed++;
|
||||
}
|
||||
} catch {
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
return { processed, errors, total: rows.length };
|
||||
return { processed, deleted, errors, total: rows.length };
|
||||
}),
|
||||
|
||||
// ─── Marquage lu/non lu AAP ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
4
todo.md
4
todo.md
@@ -187,5 +187,5 @@
|
||||
- [x] Afficher la prochaine date d'exécution dynamique dans les Paramètres (selon mode/heure/jour choisi)
|
||||
- [x] Ajouter la règle de rétention configurable dans les Paramètres (supprimer articles > N mois)
|
||||
- [x] Implémenter la purge automatique au démarrage du serveur selon la règle de rétention
|
||||
- [ ] Déployer en recette
|
||||
- [ ] Répliquer BDD recette vers production et déployer le code en production
|
||||
- [x] Déployer en recette
|
||||
- [x] Répliquer BDD recette vers production et déployer le code en production
|
||||
|
||||
Reference in New Issue
Block a user