Commit a6de8030 authored by henrik.prangel's avatar henrik.prangel
Browse files

JUT-60 && JUT-61 Add conversation validation in labeling view and button to...

JUT-60 && JUT-61 Add conversation validation in labeling view and button to remove chat from labeling
parent 98d8a962
package com.netgroup.riigibot.domain;
public class BotNameConstants {
public static final String PPA = "bot_ppa";
public static final String RIA = "bot_ria";
private BotNameConstants() {}
}
......@@ -64,6 +64,9 @@ public class Chat implements Serializable {
@Column(name = "ended")
private Instant ended;
@Column(name = "has_been_labeled")
private Boolean hasBeenLabeled;
// jhipster-needle-entity-add-field - JHipster will add fields here
public Long getId() {
return id;
......@@ -231,6 +234,14 @@ public class Chat implements Serializable {
this.needsCustomerService = needsCustomerService;
}
public Boolean getHasBeenLabeled() {
return hasBeenLabeled;
}
public void setHasBeenLabeled(Boolean hasBeenLabeled) {
this.hasBeenLabeled = hasBeenLabeled;
}
// jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here
@Override
......
......@@ -16,4 +16,6 @@ public interface ChatRepository extends JpaRepository<Chat, Long> {
Chat findBySessionId(String string);
Page<Chat> findByCommittedBotIn(Pageable pageable, List<String> string);
Page<Chat> findByEndedNotNullAndCommittedBotInAndHasBeenLabeledNullOrHasBeenLabeledFalse(Pageable pageable, List<String> string);
}
......@@ -10,5 +10,9 @@ public final class AuthoritiesConstants {
public static final String ANONYMOUS = "ROLE_ANONYMOUS";
public static final String PPA_USER = "ROLE_PPA";
public static final String RIA_USER = "ROLE_RIA";
private AuthoritiesConstants() {}
}
......@@ -51,12 +51,6 @@ public class ChatService {
return chatMapper.toDto(chat);
}
@Transactional(readOnly = true)
public Page<ChatDTO> findAllByCommittedBot(Pageable pageable, List<String> botList) {
log.debug("Request to get all Chats by committed bot");
return chatRepository.findByCommittedBotIn(pageable, botList).map(chatMapper::toDto).map(this::addLastMessageText);
}
/**
* Get all the chats.
*
......@@ -69,6 +63,21 @@ public class ChatService {
return chatRepository.findAll(pageable).map(chatMapper::toDto).map(this::addLastMessageText);
}
@Transactional(readOnly = true)
public Page<ChatDTO> getAllUnlabeledAndExpiredChatsByInstitution(Pageable pageable, List<String> botList) {
log.debug("Request to get all expired and not labeled Chats");
return chatRepository
.findByEndedNotNullAndCommittedBotInAndHasBeenLabeledNullOrHasBeenLabeledFalse(pageable, botList)
.map(chatMapper::toDto)
.map(this::addLastMessageText);
}
@Transactional(readOnly = true)
public Page<ChatDTO> findAllByCommittedBot(Pageable pageable, List<String> botList) {
log.debug("Request to get all Chats by committed bot");
return chatRepository.findByCommittedBotIn(pageable, botList).map(chatMapper::toDto).map(this::addLastMessageText);
}
/**
* Get one chat by id.
*
......
......@@ -46,6 +46,8 @@ public class ChatDTO implements Serializable {
@Size(max = 255)
private String lastRespondedBot;
private Boolean hasBeenLabeled;
public Long getId() {
return id;
}
......@@ -158,6 +160,14 @@ public class ChatDTO implements Serializable {
this.ended = ended;
}
public Boolean getHasBeenLabeled() {
return hasBeenLabeled;
}
public void setHasBeenLabeled(Boolean hasBeenLabeled) {
this.hasBeenLabeled = hasBeenLabeled;
}
@Override
public boolean equals(Object o) {
if (this == o) {
......
package com.netgroup.riigibot.web.rest;
import com.netgroup.riigibot.domain.BotNameConstants;
import com.netgroup.riigibot.security.AuthoritiesConstants;
import com.netgroup.riigibot.service.ChatService;
import com.netgroup.riigibot.service.dto.ChatDTO;
import com.netgroup.riigibot.web.rest.errors.BadRequestAlertException;
......@@ -97,19 +99,21 @@ public class ChatResource {
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
return ResponseEntity.ok().headers(headers).body(page.getContent());
}
@GetMapping("/chats/not-labeled")
public ResponseEntity<List<ChatDTO>> getAllUnlabeledAndExpiredChatsByInstitution(Pageable pageable, Authentication authentication) {
log.debug("REST request to get a page of unlabeled and expired Chats");
List<String> bots = getListOfBotsUserHasAuthorityToAccess(authentication);
Page<ChatDTO> page = chatService.getAllUnlabeledAndExpiredChatsByInstitution(pageable, bots);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
return ResponseEntity.ok().headers(headers).body(page.getContent());
}
@GetMapping("/chats/institution")
public ResponseEntity<List<ChatDTO>> getInstitutionChats(Pageable pageable, Authentication authentication) {
log.debug("REST request to get a page of Chats by institution");
List<String> bots = authentication.getAuthorities().stream().map(grantedAuthority -> {
if(grantedAuthority.getAuthority().equals("ROLE_PPA")){
return "bot_ppa";
}
if(grantedAuthority.getAuthority().equals("ROLE_RIA")){
return "bot_ria";
}
return null;
}).collect(Collectors.toList());
List<String> bots = getListOfBotsUserHasAuthorityToAccess(authentication);
Page<ChatDTO> page = chatService.findAllByCommittedBot(pageable, bots);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
......@@ -163,4 +167,16 @@ public class ChatResource {
return ResponseEntity.noContent().headers(failureAlert).build();
}
}
public List<String> getListOfBotsUserHasAuthorityToAccess(Authentication authentication) {
return authentication.getAuthorities().stream().map(grantedAuthority -> {
if (grantedAuthority.getAuthority().equals(AuthoritiesConstants.PPA_USER)) {
return BotNameConstants.PPA;
}
if (grantedAuthority.getAuthority().equals(AuthoritiesConstants.RIA_USER)) {
return BotNameConstants.RIA;
}
return null;
}).collect(Collectors.toList());
}
}
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.9.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<!--
Added new field to chat
-->
<changeSet id="20201015101000-1" author="henrik_prangel">
<addColumn tableName="chat">
<column name="has_been_labeled" type="boolean">
<constraints nullable="true"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
......@@ -23,6 +23,7 @@
<include file="config/liquibase/changelog/20200928092200_added_new_user_and_roles.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20200928151500_added_commitment_fields.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20201014152000_created_new_user_roles.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20201015101000_added_labeled_row_to_chat.xml" relativeToChangelogFile="false"/>
<!-- jhipster-needle-liquibase-add-constraints-changelog - JHipster will add liquibase constraints changelogs here -->
<!-- jhipster-needle-liquibase-add-incremental-changelog - JHipster will add incremental liquibase changelogs here -->
</databaseChangeLog>
import axios from 'axios';
import { FAILURE, REQUEST, SUCCESS } from 'app/shared/reducers/action-type.util';
import { cleanEntity } from "app/shared/util/entity-utils";
import { IChat } from "app/shared/model/chat.model";
export const ACTION_TYPES = {
SET_ACTIVE_LABELING_CHAT: 'labeling/SET_ACTIVE_LABELING_CHAT',
FETCH_ACTIVE_CHAT_MESSAGE_LIST: 'labeling/FETCH_ACTIVE_CHAT_MESSAGE_LIST',
ADD_LABELED_MESSAGE: 'labeling/ADD_LABELED_MESSAGE',
REMOVE_LABELED_MESSAGE: 'labeling/REMOVE_LABELED_MESSAGE',
SET_CHAT_AS_LABELED: 'labeling/SET_CHAT_AS_LABELED',
GET_CHATS: 'labeling/GET_CHATS',
};
......@@ -12,6 +17,7 @@ const initialState = {
chats: [],
activeLabelingChatId: "",
activeLabelingChatMessages: [],
labeledMessagesList: [],
loading: false,
errorMessage: ""
};
......@@ -24,12 +30,14 @@ export default (state: LabelingState = initialState, action): LabelingState => {
switch (action.type) {
case REQUEST(ACTION_TYPES.GET_CHATS):
case REQUEST(ACTION_TYPES.FETCH_ACTIVE_CHAT_MESSAGE_LIST):
case REQUEST(ACTION_TYPES.SET_CHAT_AS_LABELED):
return {
...state,
loading: true,
};
case FAILURE(ACTION_TYPES.GET_CHATS):
case FAILURE(ACTION_TYPES.FETCH_ACTIVE_CHAT_MESSAGE_LIST):
case FAILURE(ACTION_TYPES.SET_CHAT_AS_LABELED):
return {
...state,
loading: false,
......@@ -39,7 +47,7 @@ export default (state: LabelingState = initialState, action): LabelingState => {
return {
...state,
loading: false,
chats: action.payload.data.filter(chat => chat.ended),
chats: action.payload.data.filter(chatInList => chatInList.ended),
};
}
case SUCCESS(ACTION_TYPES.FETCH_ACTIVE_CHAT_MESSAGE_LIST): {
......@@ -49,18 +57,37 @@ export default (state: LabelingState = initialState, action): LabelingState => {
activeLabelingChatMessages: action.payload.data,
};
}
case SUCCESS(ACTION_TYPES.SET_CHAT_AS_LABELED): {
return {
...state,
loading: false,
activeLabelingChatId: "",
activeLabelingChatMessages: [],
chats: state.chats.filter(chatInList => chatInList.id !== action.payload.data.id),
};
}
case ACTION_TYPES.SET_ACTIVE_LABELING_CHAT:
return {
...state,
activeLabelingChatId: action.payload
}
case ACTION_TYPES.ADD_LABELED_MESSAGE:
return {
...state,
labeledMessagesList: [...state.labeledMessagesList, action.payload]
}
case ACTION_TYPES.REMOVE_LABELED_MESSAGE:
return {
...state,
labeledMessagesList: [...state.labeledMessagesList].filter(labeledMessage => labeledMessage.messageId !== action.payload.messageId)
}
default:
return state;
}
};
export const getExpiredChats = (page, size, sort) => async dispatch => {
const requestUrl = `api/chats/institution${sort ? `?page=${page}&size=${size}&sort=${sort}` : ''}`;
const requestUrl = `api/chats/not-labeled${sort ? `?page=${page}&size=${size}&sort=${sort}` : ''}`;
return await dispatch({
type: ACTION_TYPES.GET_CHATS,
payload: axios.get(`${requestUrl}${sort ? '&' : '?'}cacheBuster=${new Date().getTime()}`),
......@@ -75,9 +102,36 @@ export const getLabelingMessagesByChatId = chatId => {
};
};
export const setChatAsLabeled = (chatToSetAsLabeled: IChat, callback) => async dispatch => {
const requestUrl = `api/chats`;
chatToSetAsLabeled.hasBeenLabeled = true;
return await dispatch({
type: ACTION_TYPES.SET_CHAT_AS_LABELED,
payload: axios.put(requestUrl, cleanEntity(chatToSetAsLabeled)),
}).then((result) => {
callback(result)
});
};
export const setActiveLabelingChatId = (newActiveChatId) => {
return {
type: ACTION_TYPES.SET_ACTIVE_LABELING_CHAT,
payload: newActiveChatId
}
}
export const updateLabeledMessagesList = (labeledMessage, isLabeled) => {
let type;
if (isLabeled) {
type = ACTION_TYPES.ADD_LABELED_MESSAGE
} else {
type = ACTION_TYPES.REMOVE_LABELED_MESSAGE
}
return {
type,
payload: labeledMessage
}
}
import React from 'react';
import { connect, useDispatch } from 'react-redux';
import { Translate } from 'react-jhipster';
import { RouteComponentProps, Switch, Redirect } from 'react-router-dom';
import { RouteComponentProps, Switch, Redirect, useHistory } from 'react-router-dom';
import { Button } from 'reactstrap'
import { setActiveLabelingChatId, getLabelingMessagesByChatId } from './labeling.reducer';
import { setActiveLabelingChatId, getLabelingMessagesByChatId, setChatAsLabeled } from './labeling.reducer';
import ErrorBoundaryRoute from "app/shared/error/error-boundary-route";
import ChatList from "app/shared/common-components/chat-list/chat-list";
import TrainingMessages from "app/modules/training-interface/labeling/messages/training-messages";
......@@ -14,12 +14,26 @@ export interface ILabelingProp extends StateProps, RouteComponentProps<{ id: str
export const Labeling = (props: ILabelingProp) => {
const {expiredChats, activeLabelingChatId, match} = props
const dispatch = useDispatch();
const history = useHistory();
const setNewActiveLabelingChat = (chatId) => {
dispatch(setActiveLabelingChatId(chatId))
dispatch(getLabelingMessagesByChatId(chatId))
}
const setActiveChatAsLabeled = () => {
const activeChat = expiredChats.find(chat => chat.id === activeLabelingChatId)
dispatch(setChatAsLabeled(activeChat, () => {
history.push("/training/labeling")
}))
}
const updateModelAndSetChatAsLabeled = () => {
// TODO: Update model
setActiveChatAsLabeled()
}
return (
<div className="trainer-container">
<ChatList chatsToDisplay={expiredChats} activeChatId={activeLabelingChatId} onChatClick={setNewActiveLabelingChat} match={match}/>
......@@ -32,17 +46,20 @@ export const Labeling = (props: ILabelingProp) => {
</div>
<div className="room additional-content">
{activeLabelingChatId &&
<div className="room-block">
<span className="header">
<Translate
contentKey="customerService.additionalContent.actions">Conversation Details
</Translate>
<Translate contentKey="customerService.additionalContent.actions">Conversation Details</Translate>
</span>
<hr className="divider"/>
<Button className="room-button">Lisa treenimisandmed mudelisse</Button>
<Button className="room-button"> Mingi funktsionaalsus 2</Button>
<Button className="room-button"> Mingi funktsionaalsus 3</Button>
<Button className="room-button" onClick={updateModelAndSetChatAsLabeled}>
<Translate contentKey="training.labeling.addLabeledMessagesToModel">Add data to model</Translate>
</Button>
<Button className="room-button" onClick={setActiveChatAsLabeled}>
<Translate contentKey="training.labeling.removeConversationFromLabeling"> Remove conversation</Translate>
</Button>
</div>
}
</div>
</div>
);
......
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { translate, Translate } from 'react-jhipster';
import { Button } from 'reactstrap'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { updateLabeledMessagesList } from '../labeling.reducer';
import LabeledText from "app/modules/training-interface/labeling/messages/labeled-text";
export const TrainingMessage = props => {
const {trainingMessage, messageIntent} = props;
const initialMessageIntent = {name: messageIntent, example: trainingMessage.text, classes: "intent-identifier"};
const dispatch = useDispatch();
const [isLabeled, setIsLabeled] = useState(false);
const [labelingMenuOpen, setLabelingMenuOpen] = useState(false);
......@@ -89,6 +92,7 @@ export const TrainingMessage = props => {
if (!isLabeled) {
setLabelingMenuOpen(false)
}
dispatch(updateLabeledMessagesList({messageId: trainingMessage.id, intentObjects: messageIntents}, !isLabeled))
}
return (
......
import { Moment } from 'moment';
import { IMessage } from 'app/shared/model/message.model';
import { IFeedback } from 'app/shared/model/feedback.model';
......@@ -15,7 +14,8 @@ export interface IChat {
lastMessageText?: string;
committedBot?: string;
committedAdmin?: string;
needsCustomerService?: string;
needsCustomerService?: boolean;
hasBeenLabeled?: boolean;
}
export const defaultValue: Readonly<IChat> = {};
......@@ -3,7 +3,9 @@
"labeling": {
"name": "labeling",
"user": "User",
"addNewLabeledText": "Add Intent"
"addNewLabeledText": "Add Intent",
"addLabeledMessagesToModel": "Add training data to model",
"removeConversationFromLabeling": "Remove conversation from labeling"
},
"intents": {
"name": "Intent's: "
......
......@@ -3,7 +3,9 @@
"labeling": {
"name": "Märgendamine",
"user": "Kasutaja",
"addNewLabeledText": "Lisa intent"
"addNewLabeledText": "Lisa intent",
"addLabeledMessagesToModel": "Lisa treenimisandmed mudelisse",
"removeConversationFromLabeling": "Eemalda vestlus valideerimiselt"
},
"intents": {
"name": "Intent'id: "
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment