Skip to content

Commit

Permalink
Display error states when the mine/class combo does not return results (
Browse files Browse the repository at this point in the history
#92)

This commit addresses these two issues:

1)
Some mine/class combinatins may return partial data or no results at all.
This is handled by displaying informational cards so the user is made
aware of this.

2)
Some mine/class combinations may not have one or more of the default
constraints. For now the unavailable constraints are just removed from
the view.

Closes: #91

Squashed commits:
* Disable constraints for unavailable constraints in mine/class
* Handle errors when constraint path is unavailable
* Inform the user when there are no gene lengths for the mine/class combo
* Inform the user when there is no table data for the mine/class combo
* Inform the user when there is no organism summary for the mine/class combo
  • Loading branch information
JM-Mendez authored Jul 15, 2020
1 parent 14dd912 commit 704d9f9
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 31 deletions.
14 changes: 9 additions & 5 deletions src/components/Constraints/createConstraintMachine.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export const createConstraintMachine = ({
availableValues: [],
classView: '',
constraintItemsQuery,
searchIndex: null,
},
on: {
[LOCK_ALL_CONSTRAINTS]: 'constraintLimitReached',
Expand All @@ -58,12 +57,13 @@ export const createConstraintMachine = ({
},
onError: {
target: 'noConstraintItems',
actions: (ctx, event) => console.error(`FETCH: ${path}`, { ctx, event }),
actions: 'logErrorToConsole',
},
},
},
noConstraintItems: {},
noConstraintsSet: {
always: [{ target: 'noConstraintItems', cond: 'hasNoConstraintItems' }],
entry: 'resetConstraint',
on: {
[ADD_CONSTRAINT]: {
Expand All @@ -73,7 +73,7 @@ export const createConstraintMachine = ({
},
},
constraintsUpdated: {
always: [{ target: 'noConstraintsSet', cond: 'constraintListIsEmpty' }],
always: [{ target: 'noConstraintsSet', cond: 'selectedListIsEmpty' }],
on: {
[ADD_CONSTRAINT]: { actions: 'addConstraint' },
[REMOVE_CONSTRAINT]: { actions: 'removeConstraint' },
Expand Down Expand Up @@ -102,6 +102,8 @@ export const createConstraintMachine = ({

return Machine(config, {
actions: {
// @ts-ignore
logErrorToConsole: (_, event) => console.warn(event.data),
// @ts-ignore
addConstraint: assign((ctx, { constraint }) => {
ctx.selectedValues.push(constraint)
Expand All @@ -118,7 +120,6 @@ export const createConstraintMachine = ({
ctx.availableValues = data.items
ctx.classView = data.classView
ctx.selectedValues = []
ctx.searchIndex = null
}),
applyConstraint: ({ classView, constraintPath, selectedValues, availableValues }) => {
const query = {
Expand All @@ -142,9 +143,12 @@ export const createConstraintMachine = ({
},
},
guards: {
constraintListIsEmpty: (ctx) => {
selectedListIsEmpty: (ctx) => {
return ctx.selectedValues.length === 0
},
hasNoConstraintItems: (ctx) => {
return ctx.availableValues.length === 0
},
// @ts-ignore
pathMatches: (ctx, { path }) => {
return ctx.constraintPath === path
Expand Down
27 changes: 20 additions & 7 deletions src/components/DataViz/BarChart.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Machine } from 'xstate'

import { useMachineBus } from '../../machineBus'
import { barChartLoadingData } from '../loadingData/barChartData'
import { NonIdealStateWarning } from '../Shared/NonIdealStates'
import { DATA_VIZ_COLORS } from './dataVizColors'

const renderCustomTick = (isLoading) => ({ x, y, payload }) => {
Expand Down Expand Up @@ -70,12 +71,12 @@ export const BarChartMachine = Machine(
lengthSummary: [],
classView: '',
},
on: {
// Making it global ensures that we retry when the mine or class changes
[FETCH_INITIAL_SUMMARY]: { target: 'loading' },
},
states: {
idle: {
on: {
[FETCH_INITIAL_SUMMARY]: { target: 'loading' },
},
},
idle: {},
loading: {
invoke: {
id: 'fetchGeneLength',
Expand All @@ -85,11 +86,12 @@ export const BarChartMachine = Machine(
actions: 'setLengthSummary',
},
onError: {
target: 'idle',
actions: (ctx, event) => console.error('FETCH: Gene Length Chart', { ctx, event }),
target: 'noGeneLengths',
actions: 'logErrorToConsole',
},
},
},
noGeneLengths: {},
pending: {
after: {
500: 'idle',
Expand All @@ -105,6 +107,8 @@ export const BarChartMachine = Machine(
ctx.lengthSummary = data.lengthSummary
ctx.classView = data.classView
}),
// @ts-ignore
logErrorToConsole: (ctx, event) => console.warn(event.data),
},
services: {
fetchGeneLength: async (
Expand Down Expand Up @@ -174,6 +178,15 @@ export const BarChart = () => {
}
})

if (state.matches('noGeneLengths')) {
return (
<NonIdealStateWarning
title="No Gene lengths available"
description="The mine/class combination does not provide gene lengths. If you feel this is an error, please contact support"
/>
)
}

return (
<>
<ResponsiveContainer
Expand Down
32 changes: 23 additions & 9 deletions src/components/DataViz/PieChart.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import {
Text,
Tooltip,
} from 'recharts'
import { FETCH_INITIAL_SUMMARY, FETCH_UPDATED_SUMMARY } from 'src/actionConstants'
import { FETCH_INITIAL_SUMMARY } from 'src/actionConstants'
import { fetchSummary } from 'src/fetchSummary'
import { blinkingSkeletonAnimation } from 'src/styleUtils'
import { Machine } from 'xstate'

import { useMachineBus } from '../../machineBus'
import { pieChartLoadingData } from '../loadingData/pieChartData'
import { NonIdealStateWarning } from '../Shared/NonIdealStates'
import { DATA_VIZ_COLORS } from './dataVizColors'

const renderLabelContent = (props) => {
Expand Down Expand Up @@ -68,13 +69,12 @@ export const PieChartMachine = Machine(
allClassOrganisms: [],
classView: '',
},
on: {
// Making it global ensure we update the table when the mine/class changes
[FETCH_INITIAL_SUMMARY]: { target: 'loading' },
},
states: {
idle: {
on: {
[FETCH_INITIAL_SUMMARY]: { target: 'loading' },
[FETCH_UPDATED_SUMMARY]: { target: 'loading' },
},
},
idle: {},
loading: {
invoke: {
id: 'fetchPieChartValues',
Expand All @@ -89,10 +89,11 @@ export const PieChartMachine = Machine(
},
},
},
hasNoSummary: {},
// delay the finished transition to avoid quick flashes of animations
pending: {
after: {
500: 'idle',
500: [{ target: 'idle', cond: 'hasSummary' }, { target: 'hasNoSummary' }],
},
},
},
Expand All @@ -102,8 +103,12 @@ export const PieChartMachine = Machine(
// @ts-ignore
setClassItems: assign((ctx, { data }) => {
ctx.allClassOrganisms = data.summary
ctx.classView = data.classView
}),
},
guards: {
hasSummary: (ctx) => ctx.allClassOrganisms.length > 0,
},
services: {
fetchItems: async (_ctx, event) => {
const {
Expand Down Expand Up @@ -143,6 +148,15 @@ export const PieChart = () => {
const isLoading = !state.matches('idle')
const data = isLoading ? pieChartLoadingData : allClassOrganisms

if (state.matches('hasNoSummary')) {
return (
<NonIdealStateWarning
title="No Organism Summary available"
description="The mine/class combination did not return any organism summaries. If you feel this an error, please contact support"
/>
)
}

return (
<>
<ResponsiveContainer
Expand All @@ -167,7 +181,7 @@ export const PieChart = () => {
fill={isLoading ? 'var(--grey2)' : DATA_VIZ_COLORS[index % DATA_VIZ_COLORS.length]}
/>
))}
<Label classView={classView} content={renderLabelContent} />
{classView && <Label classView={classView} content={renderLabelContent} />}
{isLoading && <Label position="center" content={renderLoadingLabel} />}
</Pie>
{!isLoading && (
Expand Down
34 changes: 26 additions & 8 deletions src/components/DataViz/Table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Machine } from 'xstate'

import { sendToBus, useMachineBus } from '../../machineBus'
import { tableLoadingData } from '../loadingData/tableResults'
import { NonIdealStateWarning } from '../Shared/NonIdealStates'

const TableActionButtons = () => {
const [selectedLanguage, setLanguage] = useState('Python')
Expand Down Expand Up @@ -145,12 +146,12 @@ export const TableChartMachine = Machine(
rows: [[]],
mineUrl: '',
},
on: {
// Making it global ensure we update the table when the mine/class changes
[FETCH_INITIAL_SUMMARY]: { target: 'loading' },
},
states: {
idle: {
on: {
[FETCH_INITIAL_SUMMARY]: { target: 'loading' },
},
},
idle: {},
loading: {
invoke: {
id: 'fetchTableRows',
Expand All @@ -164,9 +165,11 @@ export const TableChartMachine = Machine(
},
},
},
noTableSummary: {},
pending: {
after: {
500: 'idle',
// Delay the rendering in case the table is currently rendering
500: [{ target: 'idle', cond: 'hasSummary' }, { target: 'noTableSummary' }],
},
},
},
Expand All @@ -179,6 +182,11 @@ export const TableChartMachine = Machine(
ctx.mineUrl = data.rootUrl
}),
},
guards: {
hasSummary: (ctx) => {
return ctx.rows[0]?.length > 0
},
},
services: {
fetchTable: async (_ctx, event) => {
const {
Expand All @@ -197,14 +205,15 @@ export const TableChartMachine = Machine(
}

const summary = await fetchTable({ rootUrl, query, page: { start: 0, size: 25 } })
const headers = summary[0].map((item) => item.column)
const hasSummary = summary.length > 0
const headers = hasSummary ? summary[0].map((item) => item.column) : []

sendToBus({ type: SET_AVAILABLE_COLUMNS, selectedPaths: headers })

return {
classView,
rootUrl,
summary,
summary: hasSummary ? summary : [[]],
}
},
},
Expand All @@ -218,6 +227,15 @@ export const Table = () => {
const isLoading = !state.matches('idle')
const rows = isLoading ? tableLoadingData : actualData

if (state.matches('noTableSummary')) {
return (
<NonIdealStateWarning
title="No Table results available"
description="The mine/class combination did not return any table data. If you feel this is an error, please contact support"
/>
)
}

return (
<>
<TableActionButtons />
Expand Down
4 changes: 4 additions & 0 deletions src/components/Layout/ConstraintSection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ const ConstraintBuilder = ({ constraintConfig, color }) => {
break
}

if (state.matches('noConstraintItems') || state.matches('loading')) {
return null
}

return (
<ConstraintServiceContext.Provider value={{ state, send }}>
<Constraint constraintIconText={label} constraintName={name} labelBorderColor={color}>
Expand Down
3 changes: 2 additions & 1 deletion src/components/Shared/NonIdealStates.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { Classes, NonIdealState } from '@blueprintjs/core'
import { IconNames } from '@blueprintjs/icons'
import React from 'react'

export const NonIdealStateWarning = ({ title = '', description = '' }) => (
export const NonIdealStateWarning = ({ title = '', description = '', className = '' }) => (
<NonIdealState
title={title}
description={description}
icon={IconNames.WARNING_SIGN}
className={className}
css={{
paddingBottom: 32,
borderRadius: 3,
Expand Down
13 changes: 12 additions & 1 deletion src/fetchSummary.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,18 @@ export const fetchSummary = async ({ rootUrl, query, path }) => {
const service = getService(rootUrl)
const q = new imjs.Query(query, service)

const fullPath = formatConstraintPath({ classView: query.from, path })
let fullPath
try {
fullPath = formatConstraintPath({ classView: query.from, path })

// make sure the path exists
await service.makePath(fullPath)
} catch (e) {
const err = new Error()
err.message = `The mine at ${rootUrl} does not contain the path ${fullPath}`

throw err
}

return await q.summarize(fullPath)
}
Expand Down

0 comments on commit 704d9f9

Please sign in to comment.