diff --git a/bot/admin/web/src/app/shared/components/sentence-training/sentence-review-request/sentence-review-request.component.html b/bot/admin/web/src/app/shared/components/sentence-training/sentence-review-request/sentence-review-request.component.html index b3af117f11..712adb59a5 100644 --- a/bot/admin/web/src/app/shared/components/sentence-training/sentence-review-request/sentence-review-request.component.html +++ b/bot/admin/web/src/app/shared/components/sentence-training/sentence-review-request/sentence-review-request.component.html @@ -19,12 +19,21 @@ > Cancel + diff --git a/bot/admin/web/src/app/shared/components/sentence-training/sentence-review-request/sentence-review-request.component.ts b/bot/admin/web/src/app/shared/components/sentence-training/sentence-review-request/sentence-review-request.component.ts index 04dbed6650..c986a6dcfb 100644 --- a/bot/admin/web/src/app/shared/components/sentence-training/sentence-review-request/sentence-review-request.component.ts +++ b/bot/admin/web/src/app/shared/components/sentence-training/sentence-review-request/sentence-review-request.component.ts @@ -30,4 +30,8 @@ export class SentenceReviewRequestComponent implements OnInit { save() { this.dialogRef.close({ status: 'confirm', description: this.description }); } + + delete() { + this.dialogRef.close({ status: 'delete' }); + } } diff --git a/bot/admin/web/src/app/shared/components/sentence-training/sentence-training-entry/sentence-training-entry.component.html b/bot/admin/web/src/app/shared/components/sentence-training/sentence-training-entry/sentence-training-entry.component.html index f50c2d0ecc..bda020c7a7 100644 --- a/bot/admin/web/src/app/shared/components/sentence-training/sentence-training-entry/sentence-training-entry.component.html +++ b/bot/admin/web/src/app/shared/components/sentence-training/sentence-training-entry/sentence-training-entry.component.html @@ -9,8 +9,8 @@ > @@ -37,7 +37,7 @@ size="tiny" class="lineFirstSmallButtonRetract" nbTooltip="Copy sentence" - (click)="copySentence(sentence)" + (click)="copySentence()" > @@ -48,7 +48,7 @@ size="tiny" class="mr-2" nbTooltip="Test this sentence in a dialog" - (click)="testDialogSentence(sentence)" + (click)="testDialogSentence()" > @@ -66,14 +66,13 @@ {{ sentence.configuration }} - -
- {{ sentence.qualifier }} + + {{ sentence.qualifier }} +
@@ -90,7 +89,7 @@ nbInput fullWidth type="text" - placeholder="{{ getSentenceAttribut(sentence, 'intentLabel') }}" + placeholder="{{ getSentenceAttribut('intentLabel') }}" (focus)="onFocus()" (blur)="onBlur($event)" (keyup.escape)="onBlur($event)" @@ -112,10 +111,14 @@ nbButton ghost nbTooltip="Intent detection probability. Click for details" - class="stats-button font-weight-normal pl-3 pr-1" - (click)="swapStatsDetails(sentence)" + class="stats-button font-weight-normal" + [class.pl-3]="!intentHasChanged()" + [class.pr-1]="!intentHasChanged()" + (click)="swapStatsDetails()" > - {{ getSentenceAttribut(sentence, 'probability') | percent: '1.0-1' }} + + {{ getSentenceAttribut('probability') | percent: '1.0-1' }} + - {{ state.intentLabelByName(i.key) }} - {{ i.value | percent: '2.2' }} + + {{ state.intentLabelByName(i.key) }} + + + {{ i.value | percent: '1.0-2' }}
@@ -194,11 +208,12 @@ nbButton nbTooltip="Validate" status="success" - ghost + [class.lineFirstLargeButtonRetract]="!isActionButtonHighlighted(Action.VALIDATE) && !isActionButtonSuggested(Action.VALIDATE)" + [ghost]="!isActionButtonHighlighted(Action.VALIDATE)" + [outline]="isActionButtonSuggested(Action.VALIDATE)" shape="round" size="large" - class="lineFirstLargeButtonRetract" - (click)="handleAction(Action.VALIDATE, sentence)" + (click)="handleAction(Action.VALIDATE)" > @@ -207,10 +222,10 @@ nbButton nbTooltip="Ask for review" status="info" - ghost + [ghost]="!isActionButtonHighlighted('review')" shape="round" size="large" - (click)="askForReview(sentence)" + (click)="askForReview()" > @@ -220,10 +235,11 @@ nbButton nbTooltip="Set as unknown" status="warning" - ghost + [ghost]="!isActionButtonHighlighted(Action.UNKNOWN)" + [outline]="isActionButtonSuggested(Action.UNKNOWN)" shape="round" size="large" - (click)="handleAction(Action.UNKNOWN, sentence)" + (click)="handleAction(Action.UNKNOWN)" > @@ -233,10 +249,11 @@ nbButton nbTooltip="Exclude from Rag handling" status="warning" - ghost + [ghost]="!isActionButtonHighlighted(Action.RAGEXCLUDED)" + [outline]="isActionButtonSuggested(Action.RAGEXCLUDED)" shape="round" size="large" - (click)="handleAction(Action.RAGEXCLUDED, sentence)" + (click)="handleAction(Action.RAGEXCLUDED)" > @@ -248,7 +265,7 @@ ghost shape="round" size="large" - (click)="handleAction(Action.DELETE, sentence)" + (click)="handleAction(Action.DELETE)" > @@ -284,7 +301,7 @@ ghost shape="round" class="lineFirstButtonRetract" - (click)="createNewIntent(sentence)" + (click)="createNewIntent()" > @@ -295,7 +312,7 @@ nbTooltip="Create new FAQ based on this sentence" ghost shape="round" - (click)="redirectToFaqManagement(sentence)" + (click)="redirectToFaqManagement()" class="ml-2" > @@ -309,7 +326,7 @@ ghost shape="round" status="info" - (click)="showDetails(sentence)" + (click)="showDetails()" > diff --git a/bot/admin/web/src/app/shared/components/sentence-training/sentence-training-entry/sentence-training-entry.component.ts b/bot/admin/web/src/app/shared/components/sentence-training/sentence-training-entry/sentence-training-entry.component.ts index 5f0e840da2..f6fc29655d 100644 --- a/bot/admin/web/src/app/shared/components/sentence-training/sentence-training-entry/sentence-training-entry.component.ts +++ b/bot/admin/web/src/app/shared/components/sentence-training/sentence-training-entry/sentence-training-entry.component.ts @@ -83,46 +83,96 @@ export class SentenceTrainingEntryComponent implements OnInit, DoCheck, OnDestro if (!this.sentence._showDialog) this.cd.markForCheck(); } - askForReview(sentence: SentenceExtended) { + isActionButtonSuggested(action: Action) { + if (this.sentence.status !== SentenceStatus.inbox) return false; + + if (action === Action.VALIDATE) { + return ( + this.sentence.classification.intentId !== Intent.unknown && + this.sentence.classification.intentId !== Intent.ragExcluded && + !this.sentence.forReview + ); + } + + if (action === Action.UNKNOWN) { + return this.sentence.classification.intentId === Intent.unknown; + } + + if (action === Action.RAGEXCLUDED) { + return this.sentence.classification.intentId === Intent.ragExcluded; + } + } + + isActionButtonHighlighted(action: Action | 'review') { + if (this.sentence.status === SentenceStatus.inbox) return false; + + if (action === 'review') { + return this.sentence.forReview; + } + + if (action === Action.VALIDATE) { + return ( + this.sentence.classification.intentId !== Intent.unknown && + this.sentence.classification.intentId !== Intent.ragExcluded && + !this.sentence.forReview && + (this.sentence.status === SentenceStatus.model || this.sentence.status === SentenceStatus.validated) + ); + } + + if (action === Action.UNKNOWN) { + return this.sentence.classification.intentId === Intent.unknown; + } + + if (action === Action.RAGEXCLUDED) { + return this.sentence.classification.intentId === Intent.ragExcluded; + } + } + + askForReview() { const dialogRef = this.nbDialogService.open(SentenceReviewRequestComponent, { context: { - beforeClassification: sentence._intentBeforeClassification || sentence.classification.intentId, - reviewComment: sentence.reviewComment + beforeClassification: this.sentence._intentBeforeClassification || this.sentence.classification.intentId, + reviewComment: this.sentence.reviewComment } }); dialogRef.onClose.subscribe((result) => { if (result && result.status === 'confirm') { - sentence.forReview = true; - sentence.reviewComment = result.description; - this.handleAction(Action.VALIDATE, sentence); + this.sentence.forReview = true; + this.sentence.reviewComment = result.description; + this.handleAction(Action.VALIDATE); + } + if (result && result.status === 'delete') { + this.sentence.forReview = false; + this.sentence.reviewComment = undefined; + this.handleAction(Action.VALIDATE, false); } }); } - async handleAction(action: Action, sentence: SentenceExtended): Promise { + async handleAction(action: Action, clearSentence = true): Promise { const actionTitle = this.getActionTitle(action); - this.setSentenceAccordingToAction(action, sentence); + this.setSentenceAccordingToAction(action); - await lastValueFrom(this.nlp.updateSentence(sentence)); + await lastValueFrom(this.nlp.updateSentence(this.sentence)); // delete old sentence when language change - if (sentence.language !== this.state.currentLocale) { - const s = sentence.clone(); + if (this.sentence.language !== this.state.currentLocale) { + const s = this.sentence.clone(); s.language = this.state.currentLocale; s.status = SentenceStatus.deleted; this.nlp.updateSentence(s).subscribe((_) => { - this.toastrService.success(`Language change to ${this.state.localeName(sentence.language)}`, 'Language change'); + this.toastrService.success(`Language change to ${this.state.localeName(this.sentence.language)}`, 'Language change'); }); } - if (this.selection?.isSelected(sentence)) { - this.selection.deselect(sentence); + if (this.selection?.isSelected(this.sentence)) { + this.selection.deselect(this.sentence); } - this.onClearSentence.emit(sentence); + if (clearSentence) this.onClearSentence.emit(this.sentence); - this.toastrService.success(truncate(sentence.text), actionTitle, { + this.toastrService.success(truncate(this.sentence.text), actionTitle, { duration: 2000, status: 'basic' }); @@ -143,37 +193,53 @@ export class SentenceTrainingEntryComponent implements OnInit, DoCheck, OnDestro } } - private setSentenceAccordingToAction(action: Action, sentence: SentenceExtended): void { + private setSentenceAccordingToAction(action: Action): void { switch (action) { case Action.DELETE: - sentence.status = SentenceStatus.deleted; + this.sentence.status = SentenceStatus.deleted; break; case Action.UNKNOWN: - sentence.classification.intentId = Intent.unknown; - sentence.classification.entities = []; - sentence.status = SentenceStatus.validated; + this.sentence.classification.intentId = Intent.unknown; + this.sentence.classification.entities = []; + this.sentence.status = SentenceStatus.validated; break; case Action.RAGEXCLUDED: - sentence.classification.intentId = Intent.ragExcluded; - sentence.classification.entities = []; - sentence.status = SentenceStatus.validated; + this.sentence.classification.intentId = Intent.ragExcluded; + this.sentence.classification.entities = []; + this.sentence.status = SentenceStatus.validated; break; case Action.VALIDATE: - const intentId = sentence.classification.intentId; + const intentId = this.sentence.classification.intentId; if (!intentId) { this.toastrService.show(`Please select an intent first`); break; } if (intentId === Intent.unknown) { - sentence.classification.intentId = Intent.unknown; - sentence.classification.entities = []; + this.sentence.classification.intentId = Intent.unknown; + this.sentence.classification.entities = []; } - sentence.status = SentenceStatus.validated; + this.sentence.status = SentenceStatus.validated; break; } } + intentHasChanged(): boolean { + return this.sentence._intentBeforeClassification && this.sentence._intentBeforeClassification !== this.sentence.classification.intentId; + } + + setSentenceIntentByName(name: string) { + if (name === Intent.unknown) { + this.setSentenceAccordingToAction(Action.UNKNOWN); + } else { + const n = name.indexOf(':') == -1 ? name : nameFromQualifiedName(name); + const intent = this.state.findIntentByName(n); + if (intent) { + this.addIntentToSentence(intent._id); + } + } + } + addIntentToSentence(intentId: string): void { const isSelected = this.selection?.isSelected(this.sentence); this.selection?.deselect(this.sentence); @@ -240,58 +306,58 @@ export class SentenceTrainingEntryComponent implements OnInit, DoCheck, OnDestro event.target.value = ''; } - swapStatsDetails(sentence: SentenceExtended): void { - sentence._showStatsDetails = !sentence._showStatsDetails; + swapStatsDetails(): void { + this.sentence._showStatsDetails = !this.sentence._showStatsDetails; } - getSentenceAttribut(sentence: SentenceExtended, category: 'intentLabel' | 'probability'): string | number { + getSentenceAttribut(category: 'intentLabel' | 'probability'): string | number { switch (category) { case 'intentLabel': - return sentence.getIntentLabel(this.state); + return this.sentence.getIntentLabel(this.state); case 'probability': - return sentence.classification.intentProbability; + return this.sentence.classification.intentProbability; } } - isSentenceSelected(sentence: SentenceExtended): boolean { - return this.selection?.isSelected(sentence); + isSentenceSelected(): boolean { + return this.selection?.isSelected(this.sentence); } - toggle(sentence: SentenceExtended): void { - this.selection?.toggle(sentence); + toggle(): void { + this.selection?.toggle(this.sentence); } - showDetails(sentence: SentenceExtended): void { - this.onDetails.emit(sentence); + showDetails(): void { + this.onDetails.emit(this.sentence); } - async copySentence(sentence) { - copyToClipboard(sentence.getText()); + async copySentence() { + copyToClipboard(this.sentence.getText()); this.toastrService.success(`Sentence copied to clipboard`, 'Clipboard'); } - testDialogSentence(sentence) { + testDialogSentence() { this.testDialogService.testSentenceDialog({ - sentenceText: sentence.text, - sentenceLocale: sentence.language + sentenceText: this.sentence.text, + sentenceLocale: this.sentence.language }); } - redirectToFaqManagement(sentence: SentenceExtended): void { - this.router.navigate(['faq/management'], { state: { question: sentence.text } }); + redirectToFaqManagement(): void { + this.router.navigate(['faq/management'], { state: { question: this.sentence.text } }); } - createNewIntent(sentence: SentenceExtended) { + createNewIntent() { const dialogRef = this.nbDialogService.open(IntentDialogComponent, { context: { create: true } }); dialogRef.onClose.subscribe((result) => { if (result && result.name) { - this.createIntent(sentence, result.name, result.label, result.description, result.category); + this.createIntent(result.name, result.label, result.description, result.category); } }); } - private createIntent(sentence: SentenceExtended, name: string, label: string, description: string, category: string): void { + private createIntent(name: string, label: string, description: string, category: string): void { if ( StateService.intentExistsInApp(this.state.currentApplication, name) || name === nameFromQualifiedName(Intent.unknown) || @@ -313,16 +379,16 @@ export class SentenceTrainingEntryComponent implements OnInit, DoCheck, OnDestro }); dialogRef.onClose.subscribe((result) => { if (result === action) { - this.saveIntent(sentence, name, label, description, category); + this.saveIntent(name, label, description, category); } }); } else { - this.saveIntent(sentence, name, label, description, category); + this.saveIntent(name, label, description, category); } } } - private saveIntent(sentence: SentenceExtended, name: string, label: string, description: string, category: string) { + private saveIntent(name: string, label: string, description: string, category: string) { this.nlp .saveIntent( new Intent(name, this.state.user.organization, [], [this.state.currentApplication._id], [], [], label, description, category) @@ -331,9 +397,9 @@ export class SentenceTrainingEntryComponent implements OnInit, DoCheck, OnDestro next: (intent) => { this.state.addIntent(intent); - sentence.classification.intentId = intent._id; - const oldSentenceIndex = this.sentences.findIndex((s) => s === sentence); - const oldSentence = sentence; + this.sentence.classification.intentId = intent._id; + const oldSentenceIndex = this.sentences.findIndex((s) => s === this.sentence); + const oldSentence = this.sentence; const newSentence = oldSentence.clone(); newSentence.classification.intentId = intent._id; diff --git a/bot/admin/web/src/app/shared/components/sentence-training/sentence-training.component.html b/bot/admin/web/src/app/shared/components/sentence-training/sentence-training.component.html index 57381634f8..43d1d9e8cb 100644 --- a/bot/admin/web/src/app/shared/components/sentence-training/sentence-training.component.html +++ b/bot/admin/web/src/app/shared/components/sentence-training/sentence-training.component.html @@ -132,7 +132,7 @@