Checkpoint: Classification IA en deux étapes (filtre pertinence + classification catégorie) intégrée dans le moteur RSS. Inclut : module aiClassifier.ts avec les deux prompts, intégration dans rssEngine.ts avec fallback sur règles, procédures tRPC reclassifyWithAI pour veille et AAP, badge IA violet dans VeilleDashboard, bouton "Classer avec l'IA" (admin), section "Analyse par l'IA" dans la boîte de dialogue détail, 7 nouveaux tests Vitest (20 tests au total). Migration BDD appliquée (4 nouveaux champs IA dans veille_items et aap_items).

This commit is contained in:
Manus
2026-06-03 05:31:58 -04:00
parent d9cded5970
commit 9c26772617
11 changed files with 1618 additions and 17 deletions

View File

@@ -0,0 +1,8 @@
ALTER TABLE `aap_items` ADD `iaRelevant` boolean;--> statement-breakpoint
ALTER TABLE `aap_items` ADD `iaCategorie` varchar(128);--> statement-breakpoint
ALTER TABLE `aap_items` ADD `iaClassifiedBy` enum('ia','rules');--> statement-breakpoint
ALTER TABLE `aap_items` ADD `iaReason` text;--> statement-breakpoint
ALTER TABLE `veille_items` ADD `iaRelevant` boolean;--> statement-breakpoint
ALTER TABLE `veille_items` ADD `iaCategorie` varchar(128);--> statement-breakpoint
ALTER TABLE `veille_items` ADD `iaClassifiedBy` enum('ia','rules');--> statement-breakpoint
ALTER TABLE `veille_items` ADD `iaReason` text;

View File

@@ -0,0 +1,918 @@
{
"version": "5",
"dialect": "mysql",
"id": "7dc085c2-224c-40d4-817b-3fa3190acef0",
"prevId": "a1057ebe-2a3e-40d5-b599-6e9abe8f38d7",
"tables": {
"aap_items": {
"name": "aap_items",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"dedupKey": {
"name": "dedupKey",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"titre": {
"name": "titre",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"categorie": {
"name": "categorie",
"type": "enum('Handicap','PA','Enfance','Précarité','Sanitaire','Autre')",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"region": {
"name": "region",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"departement": {
"name": "departement",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"departements": {
"name": "departements",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"dateCloture": {
"name": "dateCloture",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"datePublication": {
"name": "datePublication",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"lien": {
"name": "lien",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"importedAt": {
"name": "importedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"iaRelevant": {
"name": "iaRelevant",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"iaCategorie": {
"name": "iaCategorie",
"type": "varchar(128)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"iaClassifiedBy": {
"name": "iaClassifiedBy",
"type": "enum('ia','rules')",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"iaReason": {
"name": "iaReason",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"aap_items_id": {
"name": "aap_items_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"aap_items_dedupKey_unique": {
"name": "aap_items_dedupKey_unique",
"columns": [
"dedupKey"
]
}
},
"checkConstraint": {}
},
"app_settings": {
"name": "app_settings",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"key": {
"name": "key",
"type": "varchar(128)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"value": {
"name": "value",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"app_settings_id": {
"name": "app_settings_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"app_settings_key_unique": {
"name": "app_settings_key_unique",
"columns": [
"key"
]
}
},
"checkConstraint": {}
},
"ideas": {
"name": "ideas",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"userId": {
"name": "userId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"userName": {
"name": "userName",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"titre": {
"name": "titre",
"type": "varchar(512)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"message": {
"name": "message",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"statut": {
"name": "statut",
"type": "enum('ouvert','en_cours','resolu','ferme')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'ouvert'"
},
"reponseAdmin": {
"name": "reponseAdmin",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"reponduPar": {
"name": "reponduPar",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"reponduAt": {
"name": "reponduAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"ideas_id": {
"name": "ideas_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"import_logs": {
"name": "import_logs",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"fileType": {
"name": "fileType",
"type": "enum('veille','aap')",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"source": {
"name": "source",
"type": "varchar(512)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"status": {
"name": "status",
"type": "enum('success','partial','error')",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"totalRows": {
"name": "totalRows",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": 0
},
"newRows": {
"name": "newRows",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": 0
},
"skippedRows": {
"name": "skippedRows",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": 0
},
"errorMessage": {
"name": "errorMessage",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"details": {
"name": "details",
"type": "json",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"startedAt": {
"name": "startedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"completedAt": {
"name": "completedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"import_logs_id": {
"name": "import_logs_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"local_users": {
"name": "local_users",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"username": {
"name": "username",
"type": "varchar(128)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(320)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"passwordHash": {
"name": "passwordHash",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"role": {
"name": "role",
"type": "enum('admin','user','readonly')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'user'"
},
"isActive": {
"name": "isActive",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
},
"lastSignedIn": {
"name": "lastSignedIn",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"local_users_id": {
"name": "local_users_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"local_users_username_unique": {
"name": "local_users_username_unique",
"columns": [
"username"
]
}
},
"checkConstraint": {}
},
"rss_feeds": {
"name": "rss_feeds",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"url": {
"name": "url",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"feedType": {
"name": "feedType",
"type": "enum('veille','aap')",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"defaultTypeVeille": {
"name": "defaultTypeVeille",
"type": "enum('reglementaire','concurrentielle','technologique','generale')",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"defaultCategorieAap": {
"name": "defaultCategorieAap",
"type": "enum('Handicap','PA','Enfance','Précarité','Sanitaire','Autre')",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"autoRules": {
"name": "autoRules",
"type": "json",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"isActive": {
"name": "isActive",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"lastFetchedAt": {
"name": "lastFetchedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"lastFetchStatus": {
"name": "lastFetchStatus",
"type": "enum('ok','error','pending')",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'pending'"
},
"lastFetchError": {
"name": "lastFetchError",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"rss_feeds_id": {
"name": "rss_feeds_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"rss_settings": {
"name": "rss_settings",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"fetchIntervalMinutes": {
"name": "fetchIntervalMinutes",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 360
},
"scheduledTime": {
"name": "scheduledTime",
"type": "varchar(5)",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'06:00'"
},
"fetchMode": {
"name": "fetchMode",
"type": "enum('interval','scheduled')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'scheduled'"
},
"autoFetchEnabled": {
"name": "autoFetchEnabled",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"rss_settings_id": {
"name": "rss_settings_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"openId": {
"name": "openId",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(320)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"loginMethod": {
"name": "loginMethod",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"role": {
"name": "role",
"type": "enum('user','admin')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'user'"
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
},
"lastSignedIn": {
"name": "lastSignedIn",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"users_id": {
"name": "users_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"users_openId_unique": {
"name": "users_openId_unique",
"columns": [
"openId"
]
}
},
"checkConstraint": {}
},
"veille_items": {
"name": "veille_items",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"dedupKey": {
"name": "dedupKey",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"titre": {
"name": "titre",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"categorie": {
"name": "categorie",
"type": "varchar(128)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"niveau": {
"name": "niveau",
"type": "varchar(128)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"territoire": {
"name": "territoire",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"territoires": {
"name": "territoires",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"resume": {
"name": "resume",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"source": {
"name": "source",
"type": "varchar(512)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"passage": {
"name": "passage",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"lien": {
"name": "lien",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"typeVeille": {
"name": "typeVeille",
"type": "enum('reglementaire','concurrentielle','technologique','generale')",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"datePublication": {
"name": "datePublication",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"importedAt": {
"name": "importedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"iaRelevant": {
"name": "iaRelevant",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"iaCategorie": {
"name": "iaCategorie",
"type": "varchar(128)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"iaClassifiedBy": {
"name": "iaClassifiedBy",
"type": "enum('ia','rules')",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"iaReason": {
"name": "iaReason",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"veille_items_id": {
"name": "veille_items_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"veille_items_dedupKey_unique": {
"name": "veille_items_dedupKey_unique",
"columns": [
"dedupKey"
]
}
},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}

View File

@@ -50,6 +50,13 @@
"when": 1777805205749,
"tag": "0006_fixed_lady_deathstrike",
"breakpoints": true
},
{
"idx": 7,
"version": "5",
"when": 1780478622510,
"tag": "0007_clammy_blue_marvel",
"breakpoints": true
}
]
}

View File

@@ -77,6 +77,11 @@ export const veilleItems = mysqlTable("veille_items", {
// Date extraite de la colonne Source (qui contient parfois une date ISO)
datePublication: timestamp("datePublication"),
importedAt: timestamp("importedAt").defaultNow().notNull(),
// Classification IA
iaRelevant: boolean("iaRelevant"),
iaCategorie: varchar("iaCategorie", { length: 128 }),
iaClassifiedBy: mysqlEnum("iaClassifiedBy", ["ia", "rules"]),
iaReason: text("iaReason"),
});
export type VeilleItem = typeof veilleItems.$inferSelect;
@@ -97,6 +102,11 @@ export const aapItems = mysqlTable("aap_items", {
datePublication: timestamp("datePublication"),
lien: text("lien"),
importedAt: timestamp("importedAt").defaultNow().notNull(),
// Classification IA
iaRelevant: boolean("iaRelevant"),
iaCategorie: varchar("iaCategorie", { length: 128 }),
iaClassifiedBy: mysqlEnum("iaClassifiedBy", ["ia", "rules"]),
iaReason: text("iaReason"),
});
export type AapItem = typeof aapItems.$inferSelect;