1717package ai.tock.bot.admin
1818
1919import ai.tock.bot.admin.FaqAdminService.FAQ_CATEGORY
20+ import ai.tock.bot.admin.annotation.*
2021import ai.tock.bot.admin.answer.AnswerConfiguration
2122import ai.tock.bot.admin.answer.AnswerConfigurationType.builtin
2223import ai.tock.bot.admin.answer.AnswerConfigurationType.script
@@ -46,6 +47,7 @@ import ai.tock.bot.admin.story.dump.*
4647import ai.tock.bot.admin.user.UserReportDAO
4748import ai.tock.bot.connector.ConnectorType
4849import ai.tock.bot.definition.IntentWithoutNamespace
50+ import ai.tock.bot.engine.action.Action
4951import ai.tock.bot.engine.config.SatisfactionIntent
5052import ai.tock.bot.engine.dialog.Dialog
5153import ai.tock.bot.engine.dialog.DialogFlowDAO
@@ -62,6 +64,7 @@ import ai.tock.nlp.front.shared.config.*
6264import ai.tock.nlp.front.shared.config.ClassifiedSentenceStatus.model
6365import ai.tock.nlp.front.shared.config.ClassifiedSentenceStatus.validated
6466import ai.tock.shared.*
67+ import ai.tock.shared.exception.rest.NotFoundException
6568import ai.tock.shared.security.UserLogin
6669import ai.tock.shared.security.key.HasSecretKey
6770import ai.tock.shared.security.key.SecretKey
@@ -70,6 +73,7 @@ import ai.tock.translator.*
7073import com.github.salomonbrys.kodein.instance
7174import mu.KotlinLogging
7275import org.litote.kmongo.Id
76+ import org.litote.kmongo.newId
7377import org.litote.kmongo.toId
7478import java.time.Instant
7579import java.util.*
@@ -149,6 +153,200 @@ object BotAdminService {
149153 }
150154 }
151155
156+ fun addCommentToAnnotation (
157+ dialogId : String ,
158+ actionId : String ,
159+ eventDTO : BotAnnotationEventDTO ,
160+ user : String
161+ ): BotAnnotationEvent {
162+ if (eventDTO.type != BotAnnotationEventType .COMMENT ) {
163+ throw IllegalArgumentException (" Only COMMENT events are allowed" )
164+ }
165+
166+ require(! eventDTO.comment.isNullOrBlank()) { " Comment is required and cannot be blank for COMMENT event type" }
167+
168+ val annotation = dialogReportDAO.findAnnotation(dialogId, actionId)
169+ ? : throw IllegalStateException (" Annotation not found" )
170+
171+ val event = BotAnnotationEventComment (
172+ eventId = newId(),
173+ creationDate = Instant .now(),
174+ lastUpdateDate = Instant .now(),
175+ user = user,
176+ comment = eventDTO.comment ? : throw IllegalArgumentException (" Comment required" )
177+ )
178+
179+ dialogReportDAO.addAnnotationEvent(dialogId, actionId, event)
180+
181+ return event.copy(canEdit = true )
182+ }
183+
184+ fun updateAnnotationEvent (
185+ dialogId : String ,
186+ actionId : String ,
187+ eventId : String ,
188+ eventDTO : BotAnnotationEventDTO ,
189+ user : String
190+ ): BotAnnotationEvent {
191+ val existingEvent = dialogReportDAO.getAnnotationEvent(dialogId, actionId, eventId)
192+ ? : throw IllegalArgumentException (" Event not found" )
193+
194+ if (existingEvent.type != BotAnnotationEventType .COMMENT ) {
195+ throw IllegalArgumentException (" Only comment events can be updated" )
196+ }
197+
198+ if (eventDTO.type != BotAnnotationEventType .COMMENT ) {
199+ throw IllegalArgumentException (" Event type must be COMMENT" )
200+ }
201+
202+ require(eventDTO.comment != null ) { " Comment must be provided" }
203+
204+ val annotation = dialogReportDAO.findAnnotation(dialogId, actionId)
205+ ? : throw IllegalStateException (" Annotation not found" )
206+
207+ val existingCommentEvent = existingEvent as BotAnnotationEventComment
208+ val updatedEvent = existingCommentEvent.copy(
209+ comment = eventDTO.comment!! ,
210+ lastUpdateDate = Instant .now()
211+ )
212+
213+ dialogReportDAO.updateAnnotationEvent(dialogId, actionId, eventId, updatedEvent)
214+
215+ return updatedEvent.copy(canEdit = updatedEvent.user == user)
216+ }
217+
218+ fun deleteAnnotationEvent (
219+ dialogId : String ,
220+ actionId : String ,
221+ eventId : String ,
222+ user : String
223+ ) {
224+ val existingEvent = dialogReportDAO.getAnnotationEvent(dialogId, actionId, eventId)
225+ ? : throw IllegalArgumentException (" Event not found" )
226+
227+ if (existingEvent.type != BotAnnotationEventType .COMMENT ) {
228+ throw IllegalArgumentException (" Only comment events can be deleted" )
229+ }
230+
231+ dialogReportDAO.deleteAnnotationEvent(dialogId, actionId, eventId)
232+ }
233+
234+ fun updateAnnotation (
235+ dialogId : String ,
236+ actionId : String ,
237+ annotationDTO : BotAnnotationDTO ,
238+ user : String
239+ ): BotAnnotation {
240+ val existingAnnotation = dialogReportDAO.findAnnotation(dialogId, actionId)
241+ ? : throw IllegalStateException (" Annotation not found" )
242+
243+ val events = mutableListOf<BotAnnotationEvent >()
244+
245+ if (existingAnnotation.state != annotationDTO.state) {
246+ events.add(
247+ BotAnnotationEventState (
248+ eventId = newId(),
249+ creationDate = Instant .now(),
250+ lastUpdateDate = Instant .now(),
251+ user = user,
252+ before = existingAnnotation.state.name,
253+ after = annotationDTO.state.name
254+ )
255+ )
256+ existingAnnotation.state = annotationDTO.state
257+ }
258+
259+ if (existingAnnotation.reason != annotationDTO.reason) {
260+ events.add(
261+ BotAnnotationEventReason (
262+ eventId = newId(),
263+ creationDate = Instant .now(),
264+ lastUpdateDate = Instant .now(),
265+ user = user,
266+ before = existingAnnotation.reason?.name,
267+ after = annotationDTO.reason?.name
268+ )
269+ )
270+ existingAnnotation.reason = annotationDTO.reason
271+ }
272+
273+ if (existingAnnotation.groundTruth != annotationDTO.groundTruth) {
274+ events.add(
275+ BotAnnotationEventGroundTruth (
276+ eventId = newId(),
277+ creationDate = Instant .now(),
278+ lastUpdateDate = Instant .now(),
279+ user = user,
280+ before = existingAnnotation.groundTruth,
281+ after = annotationDTO.groundTruth
282+ )
283+ )
284+ existingAnnotation.groundTruth = annotationDTO.groundTruth
285+ }
286+
287+ if (existingAnnotation.description != annotationDTO.description) {
288+ events.add(
289+ BotAnnotationEventDescription (
290+ eventId = newId(),
291+ creationDate = Instant .now(),
292+ lastUpdateDate = Instant .now(),
293+ user = user,
294+ before = existingAnnotation.description,
295+ after = annotationDTO.description
296+ )
297+ )
298+ existingAnnotation.description = annotationDTO.description
299+ }
300+
301+ existingAnnotation.lastUpdateDate = Instant .now()
302+ existingAnnotation.events.addAll(events)
303+
304+ dialogReportDAO.insertAnnotation(dialogId, actionId, existingAnnotation)
305+
306+ return existingAnnotation.copy(
307+ events = existingAnnotation.events.map { event ->
308+ if (event is BotAnnotationEventComment ) {
309+ event.copy(canEdit = event.user == user)
310+ } else {
311+ event
312+ }
313+ }.toMutableList()
314+ )
315+ }
316+
317+ fun createAnnotation (
318+ dialogId : String ,
319+ actionId : String ,
320+ annotationDTO : BotAnnotationDTO ,
321+ user : String
322+ ): BotAnnotation {
323+ if (dialogReportDAO.annotationExists(dialogId, actionId)) {
324+ throw IllegalStateException (" An annotation already exists for this action" )
325+ }
326+
327+ val annotation = BotAnnotation (
328+ state = annotationDTO.state,
329+ reason = annotationDTO.reason,
330+ description = annotationDTO.description,
331+ groundTruth = annotationDTO.groundTruth,
332+ events = mutableListOf (),
333+ lastUpdateDate = Instant .now()
334+ )
335+
336+ val event = BotAnnotationEventState (
337+ eventId = newId(),
338+ creationDate = Instant .now(),
339+ lastUpdateDate = Instant .now(),
340+ user = user,
341+ before = null ,
342+ after = annotationDTO.state.name
343+ )
344+
345+ annotation.events.add(event)
346+ dialogReportDAO.insertAnnotation(dialogId, actionId, annotation)
347+ return annotation
348+ }
349+
152350 fun createOrGetIntent (
153351 namespace : String ,
154352 intentName : String ,
@@ -216,6 +414,41 @@ object BotAdminService {
216414 }
217415 }
218416
417+ fun searchWithCommentRights (query : DialogsSearchQuery , userLogin : String ): DialogReportQueryResult {
418+ val result = search(query)
419+ return result.copy(
420+ dialogs = result.dialogs.map { dialog ->
421+ processAnnotationsForUser(dialog, userLogin)
422+ }
423+ )
424+ }
425+
426+ fun getDialogWithCommentRights (id : Id <Dialog >, userLogin : String ): DialogReport ? {
427+ return dialogReportDAO.getDialog(id)?.let { dialog ->
428+ processAnnotationsForUser(dialog, userLogin)
429+ }
430+ }
431+
432+ private fun processAnnotationsForUser (dialog : DialogReport , userLogin : String ): DialogReport {
433+ return dialog.copy(
434+ actions = dialog.actions.map { action ->
435+ action.copy(
436+ annotation = action.annotation?.let { annotation ->
437+ annotation.copy(
438+ events = annotation.events.map { event ->
439+ if (event is BotAnnotationEventComment ) {
440+ event.copy(canEdit = event.user == userLogin)
441+ } else {
442+ event
443+ }
444+ }.toMutableList()
445+ )
446+ }
447+ )
448+ }
449+ )
450+ }
451+
219452 fun getIntentsInDialogs (namespace : String ,nlpModel : String ) : Set <String >{
220453 return dialogReportDAO.intents(namespace,nlpModel)
221454 }
0 commit comments