Skip to content

Commit

Permalink
feat(misunderstood): add thumbs down filter (#4310)
Browse files Browse the repository at this point in the history
* add thumbs down filter to misunderstood

* improve code reusability and fix tslint errors

* change migration file version and fix pull request requested changes

* FilteringOptions type
  • Loading branch information
davidvitora committed Jan 8, 2021
1 parent 3749888 commit 8604354
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 41 deletions.
12 changes: 6 additions & 6 deletions modules/misunderstood/src/backend/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ export default async (bp: typeof sdk, db: Db) => {
'/events/count',
asyncMiddleware(async (req: Request, res: Response) => {
const { botId } = req.params
const { language, startDate, endDate } = extractQuery(req.query)
const { language, startDate, endDate, reason } = extractQuery(req.query)

try {
const data = await db.countEvents(botId, language, { startDate, endDate })
const data = await db.countEvents(botId, language, { startDate, endDate, reason })
res.json(data)
} catch (err) {
throw new StandardError(err)
Expand All @@ -65,10 +65,10 @@ export default async (bp: typeof sdk, db: Db) => {
`/events/:status(${FLAGGED_MESSAGE_STATUSES.join('|')})`,
asyncMiddleware(async (req: Request, res: Response) => {
const { botId, status } = req.params
const { language, startDate, endDate } = extractQuery(req.query)
const { language, startDate, endDate, reason } = extractQuery(req.query)

try {
const data = await db.listEvents(botId, language, status, { startDate, endDate })
const data = await db.listEvents(botId, language, status, { startDate, endDate, reason })
res.json(data)
} catch (err) {
throw new StandardError('Error listing events', err)
Expand Down Expand Up @@ -124,10 +124,10 @@ export default async (bp: typeof sdk, db: Db) => {
}

const extractQuery = query => {
const { language, start, end } = query
const { language, start, end, reason } = query
const startDate = start && unixToDate(start)
const endDate = end && unixToDate(end)

return { language, startDate, endDate }
return { language, startDate, endDate, reason }
}
}
33 changes: 20 additions & 13 deletions modules/misunderstood/src/backend/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
FLAGGED_MESSAGE_STATUSES,
FLAG_REASON,
ResolutionData,
RESOLUTION_TYPE
RESOLUTION_TYPE,
FilteringOptions
} from '../types'

import applyChanges from './applyChanges'
Expand Down Expand Up @@ -62,17 +63,13 @@ export default class Db {
botId: string,
language: string,
status: FLAGGED_MESSAGE_STATUS,
options?: { startDate: Date; endDate: Date }
options?: FilteringOptions
): Promise<DbFlaggedEvent[]> {
const { startDate, endDate } = options || {}

const query = this.knex(TABLE_NAME)
.select('*')
.where({ botId, language, status })

if (startDate && endDate) {
query.andWhere(this.knex.date.isBetween('updatedAt', startDate, endDate))
}
this.filterQuery(query, options)

const data: DbFlaggedEvent[] = await query.orderBy('updatedAt', 'desc')

Expand All @@ -85,17 +82,13 @@ export default class Db {
}))
}

async countEvents(botId: string, language: string, options?: { startDate: Date; endDate: Date }) {
const { startDate, endDate } = options || {}

async countEvents(botId: string, language: string, options?: FilteringOptions) {
const query = this.knex(TABLE_NAME)
.where({ botId, language })
.select('status')
.count({ count: 'id' })

if (startDate && endDate) {
query.andWhere(this.knex.date.isBetween('updatedAt', startDate, endDate))
}
this.filterQuery(query, options)

const data: { status: string; count: number }[] = await query.groupBy('status')

Expand Down Expand Up @@ -178,4 +171,18 @@ export default class Db {
applyChanges(botId: string) {
return applyChanges(this.bp, botId, TABLE_NAME)
}

filterQuery(query, options?: FilteringOptions) {
const { startDate, endDate, reason } = options || {}

if (startDate && endDate) {
query.andWhere(this.knex.date.isBetween('updatedAt', startDate, endDate))
}

if (reason === 'thumbs_down') {
query.andWhere({ reason })
} else if (reason && reason !== 'thumbs_down') {
query.andWhereNot('reason', 'thumbs_down')
}
}
}
2 changes: 1 addition & 1 deletion modules/misunderstood/src/backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const onServerReady = async (bp: typeof sdk) => {
l => l && l !== 'n/a'
)[0],
preview: event.preview,
reason: 'auto_hook' as FLAG_REASON
reason: 'thumbs_down' as FLAG_REASON
}

await db.addEvent(data)
Expand Down
5 changes: 4 additions & 1 deletion modules/misunderstood/src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"done": "Done",
"flaggedByAction": "Flagged by action",
"flaggedByHook": "Flagged by hook",
"flaggedByThumbsDown": "Flagged by thumbs down",
"fullName": "Misunderstood",
"goal": "Goal",
"ignore": "Ignore",
Expand Down Expand Up @@ -42,5 +43,7 @@
"whatIsMessageType": "What is this message type?",
"couldNotLoadConversation": "Could not load conversation",
"conversationDeleted": "This conversation was too old and has been deleted",
"dateRange": "Date Range"
"dateRange": "Date Range",
"misunderstood": "Misunderstood",
"qnaThumbsDown": "QNA Thumbs Down"
}
5 changes: 4 additions & 1 deletion modules/misunderstood/src/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"done": "Listo",
"flaggedByAction": "Marcado por acción",
"flaggedByHook": "Marcago por hook",
"flaggedByThumbsDown": "Marcago por desaprobando",
"fullName": "Entrenamiento",
"goal": "Goal",
"ignore": "Ignorar",
Expand Down Expand Up @@ -42,5 +43,7 @@
"whatIsMessageType": "Cual es el tipo de mensaje?",
"couldNotLoadConversation": "No se pudo cargar la conversación",
"conversationDeleted": "Esta conversación era demasiado antigua y se eliminó",
"dateRange": "Rango de fechas"
"dateRange": "Rango de fechas",
"misunderstood": "sin entender",
"qnaThumbsDown": "QNA desaprobando"
}
5 changes: 4 additions & 1 deletion modules/misunderstood/src/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"done": "Terminé",
"flaggedByAction": "Signalé par action",
"flaggedByHook": "Signalé par hook",
"flaggedByThumbsDown": "Signalé par désapprobateur",
"fullName": "Incompris",
"goal": "Objectif",
"ignore": "Ignorer",
Expand Down Expand Up @@ -42,5 +43,7 @@
"whatIsMessageType": "Quel est ce type de message?",
"couldNotLoadConversation": "Impossible de trouver la conversation",
"conversationDeleted": "La conversation était trop vieille et a été effacée",
"dateRange": "Plage de dates"
"dateRange": "Plage de dates",
"misunderstood": "Incompris",
"qnaThumbsDown": "QNA désapprobateur"
}
9 changes: 8 additions & 1 deletion modules/misunderstood/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export const FLAGGED_MESSAGE_STATUSES = Object.values(FLAGGED_MESSAGE_STATUS)
export enum FLAG_REASON {
auto_hook = 'auto_hook',
action = 'action',
manual = 'manual'
manual = 'manual',
thumbs_down = 'thumbs_down'
}

export enum RESOLUTION_TYPE {
Expand Down Expand Up @@ -53,3 +54,9 @@ export interface ResolutionData {
resolution: string | null
resolutionParams?: object | null
}

export type FilteringOptions = Partial<{
startDate: Date
endDate: Date
reason?: string
}>
12 changes: 7 additions & 5 deletions modules/misunderstood/src/views/full/ApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { DateRange } from '@blueprintjs/datetime'
import { AxiosRequestConfig, AxiosStatic } from 'axios'
import moment from 'moment'

import { FLAGGED_MESSAGE_STATUS, ResolutionData, RESOLUTION_TYPE } from '../../types'
import { FLAGGED_MESSAGE_STATUS, FLAG_REASON, ResolutionData, RESOLUTION_TYPE } from '../../types'

const MODULE_URL_PREFIX = '/mod/misunderstood'

Expand All @@ -27,26 +27,28 @@ class ApiClient {
return this.post(MODULE_URL_PREFIX + url, data, config)
}

getEventCounts(language: string, dateRange?: DateRange) {
getEventCounts(language: string, dateRange?: DateRange, reason?: FLAG_REASON) {
const { start, end } = this.getRangeUnix(dateRange)

return this.getForModule('/events/count', {
params: {
language,
start,
end
end,
reason
}
})
}

getEvents(language: string, status: string, dateRange?: DateRange) {
getEvents(language: string, status: string, dateRange?: DateRange, reason?: FLAG_REASON) {
const { start, end } = this.getRangeUnix(dateRange)

return this.getForModule(`/events/${status}`, {
params: {
language,
start,
end
end,
reason
}
})
}
Expand Down
74 changes: 62 additions & 12 deletions modules/misunderstood/src/views/full/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Container, SidePanel, SplashScreen } from 'botpress/ui'
import classnames from 'classnames'
import React from 'react'

import { FlaggedEvent, FLAGGED_MESSAGE_STATUS, ResolutionData } from '../../types'
import { FlaggedEvent, FLAGGED_MESSAGE_STATUS, FLAG_REASON, ResolutionData } from '../../types'

import style from './style.scss'
import ApiClient from './ApiClient'
Expand All @@ -31,6 +31,7 @@ interface State {
selectedEvent: FlaggedEvent | null
eventNotFound: boolean
dateRange?: DateRange
reason?: FLAG_REASON
}

const shortcuts = date.createDateRangeShortcuts()
Expand All @@ -45,7 +46,8 @@ export default class MisunderstoodMainView extends React.Component<Props, State>
selectedEventIndex: null,
selectedEvent: null,
eventNotFound: false,
dateRange: undefined
dateRange: undefined,
reason: undefined
}

apiClient: ApiClient
Expand All @@ -55,12 +57,12 @@ export default class MisunderstoodMainView extends React.Component<Props, State>
this.apiClient = new ApiClient(props.bp.axios)
}

fetchEventCounts(language: string, dataRange?: DateRange) {
return this.apiClient.getEventCounts(language, dataRange ?? this.state.dateRange)
fetchEventCounts(language: string, dataRange?: DateRange, reason?: FLAG_REASON) {
return this.apiClient.getEventCounts(language, dataRange ?? this.state.dateRange, reason)
}

fetchEvents(language: string, status: string, dataRange?: DateRange) {
return this.apiClient.getEvents(language, status, dataRange || this.state.dateRange)
fetchEvents(language: string, status: string, dataRange?: DateRange, reason?: FLAG_REASON) {
return this.apiClient.getEvents(language, status, dataRange || this.state.dateRange, reason)
}

async fetchEvent(id: string) {
Expand All @@ -86,8 +88,12 @@ export default class MisunderstoodMainView extends React.Component<Props, State>
})
}

updateEventsCounts = async (language?: string, dateRange?: DateRange) => {
const eventCounts = await this.fetchEventCounts(language || this.state.language, dateRange || this.state.dateRange)
updateEventsCounts = async (language?: string, dateRange?: DateRange, reason?: FLAG_REASON) => {
const eventCounts = await this.fetchEventCounts(
language || this.state.language,
dateRange || this.state.dateRange,
reason !== undefined ? reason : this.state.reason
)
await this.setStateP({ eventCounts })
}

Expand Down Expand Up @@ -188,19 +194,30 @@ export default class MisunderstoodMainView extends React.Component<Props, State>
}
}

handleDateChange = async (dateRange: DateRange) => {
const eventCounts = await this.fetchEventCounts(this.state.language, dateRange || this.state.dateRange)
fetchEventsAndCounts = async (language: string, dataRange?: DateRange, reason?: FLAG_REASON) => {
const eventCounts = await this.fetchEventCounts(
language || this.state.language,
dataRange || this.state.dateRange,
reason !== undefined ? reason : this.state.reason
)
const events = await this.fetchEvents(
this.state.language,
language || this.state.language,
this.state.selectedStatus,
dateRange || this.state.dateRange
dataRange || this.state.dateRange,
reason !== undefined ? reason : this.state.reason
)

const event = null
if (events && events.length) {
const event = await this.fetchEvent(events[0].id)
}

return { eventCounts, events, event }
}

handleDateChange = async (dateRange: DateRange) => {
const { eventCounts, events, event } = await this.fetchEventsAndCounts(this.state.language, dateRange)

await this.setStateP({
dateRange,
events,
Expand All @@ -211,6 +228,25 @@ export default class MisunderstoodMainView extends React.Component<Props, State>
})
}

handleReasonChange = async (reason: FLAG_REASON) => {
reason = this.state.reason !== reason ? reason : null

const { eventCounts, events, event } = await this.fetchEventsAndCounts(
this.state.language,
this.state.dateRange,
reason
)

await this.setStateP({
events,
selectedEventIndex: 0,
selectedEvent: event,
eventNotFound: !event,
eventCounts,
reason
})
}

render() {
const { eventCounts, selectedStatus, events, selectedEventIndex, selectedEvent, eventNotFound } = this.state

Expand All @@ -222,6 +258,20 @@ export default class MisunderstoodMainView extends React.Component<Props, State>
return (
<Container sidePanelWidth={320}>
<SidePanel>
<div className={style.filterContainer}>
<Button
className={(this.state.reason === FLAG_REASON.auto_hook && 'selected') || ''}
onClick={() => this.handleReasonChange(FLAG_REASON.auto_hook)}
>
{lang.tr('module.misunderstood.misunderstood').toUpperCase()}
</Button>
<Button
className={(this.state.reason === FLAG_REASON.thumbs_down && 'selected') || ''}
onClick={() => this.handleReasonChange(FLAG_REASON.thumbs_down)}
>
{lang.tr('module.misunderstood.qnaThumbsDown').toUpperCase()}
</Button>
</div>
<Popover usePortal={true} position={'bottom-right'}>
<Button icon="calendar" className={style.filterItem}>
{lang.tr('module.misunderstood.dateRange')}
Expand Down
13 changes: 13 additions & 0 deletions modules/misunderstood/src/views/full/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@
margin: 10px;
}

.filterContainer {
margin: 10px 10px 0px 10px;
span {
font-size: 10pt;
}
:global(.selected),
:global(.selected):hover {
box-shadow: inset 0 0 0 1px rgba(16, 22, 26, 0.2), inset 0 1px 2px rgba(16, 22, 26, 0.2);
background-color: #d8e1e8;
background-image: none;
}
}

.contentFixed {
flex-grow: 0;
flex-shrink: 0;
Expand Down
1 change: 1 addition & 0 deletions modules/misunderstood/src/views/full/style.scss.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ interface CssExports {
'applyAllButton': string;
'contentFixed': string;
'contentStretch': string;
'filterContainer': string;
'filterItem': string;
'headerTabs': string;
'mainView': string;
Expand Down

0 comments on commit 8604354

Please sign in to comment.