Skip to content

Commit

Permalink
refactor(app): add robot serial number to mixpanel analytics (#14436)
Browse files Browse the repository at this point in the history
* refactor(app): add robot serial number to mixpanel analytics

closes RAUT-899
  • Loading branch information
ncdiehl11 authored Feb 9, 2024
1 parent 180d9ca commit 0c19564
Show file tree
Hide file tree
Showing 25 changed files with 175 additions and 68 deletions.
3 changes: 2 additions & 1 deletion app/src/organisms/ChooseProtocolSlideout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ export function ChooseProtocolSlideoutComponent(
: []

const { trackCreateProtocolRunEvent } = useTrackCreateProtocolRunEvent(
selectedProtocol
selectedProtocol,
name
)

const {
Expand Down
5 changes: 3 additions & 2 deletions app/src/organisms/ChooseRobotToRunProtocolSlideout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ export function ChooseRobotToRunProtocolSlideoutComponent(
mostRecentAnalysis,
} = storedProtocolData

const [selectedRobot, setSelectedRobot] = React.useState<Robot | null>(null)
const { trackCreateProtocolRunEvent } = useTrackCreateProtocolRunEvent(
storedProtocolData
storedProtocolData,
selectedRobot?.name ?? ''
)

const [selectedRobot, setSelectedRobot] = React.useState<Robot | null>(null)
const offsetCandidates = useOffsetCandidatesForAnalysis(
mostRecentAnalysis,
selectedRobot?.ip ?? null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ function MenuDropdown(props: MenuDropdownProps): JSX.Element {
closeOverflowMenu(e)
}
const trackEvent = useTrackEvent()
const { trackProtocolRunEvent } = useTrackProtocolRunEvent(runId)
const { trackProtocolRunEvent } = useTrackProtocolRunEvent(runId, robotName)
const { reset } = useRunControls(runId, onResetSuccess)
const { deleteRun } = useDeleteRunMutation()

Expand Down
5 changes: 3 additions & 2 deletions app/src/organisms/Devices/ProtocolRun/ProtocolRunHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export function ProtocolRunHeader({
isProtocolAnalyzing,
} = useProtocolDetailsForRun(runId)

const { trackProtocolRunEvent } = useTrackProtocolRunEvent(runId)
const { trackProtocolRunEvent } = useTrackProtocolRunEvent(runId, robotName)
const robotAnalyticsData = useRobotAnalyticsData(robotName)
const isRobotViewable = useIsRobotViewable(robotName)
const runStatus = useRunStatus(runId)
Expand Down Expand Up @@ -453,6 +453,7 @@ export function ProtocolRunHeader({
<ConfirmCancelModal
onClose={() => setShowConfirmCancelModal(false)}
runId={runId}
robotName={robotName}
/>
) : null}
{showDropTipWizard &&
Expand Down Expand Up @@ -575,7 +576,7 @@ function ActionButton(props: ActionButtonProps): JSX.Element {
enabled: runStatus != null && START_RUN_STATUSES.includes(runStatus),
})?.data?.data ?? []
const trackEvent = useTrackEvent()
const { trackProtocolRunEvent } = useTrackProtocolRunEvent(runId)
const { trackProtocolRunEvent } = useTrackProtocolRunEvent(runId, robotName)
const [targetProps, tooltipProps] = useHoverTooltip()
const {
play,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,9 +406,11 @@ describe('ProtocolRunHeader', () => {
when(mockUseProtocolDetailsForRun)
.calledWith(RUN_ID)
.mockReturnValue(PROTOCOL_DETAILS)
when(mockUseTrackProtocolRunEvent).calledWith(RUN_ID).mockReturnValue({
trackProtocolRunEvent: mockTrackProtocolRunEvent,
})
when(mockUseTrackProtocolRunEvent)
.calledWith(RUN_ID, ROBOT_NAME)
.mockReturnValue({
trackProtocolRunEvent: mockTrackProtocolRunEvent,
})
when(mockUseDismissCurrentRunMutation)
.calledWith()
.mockReturnValue({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,11 @@ describe('HistoricalProtocolRunOverflowMenu', () => {
deleteRun: jest.fn(),
} as any)
)
when(mockUseTrackProtocolRunEvent).calledWith(RUN_ID).mockReturnValue({
trackProtocolRunEvent: mockTrackProtocolRunEvent,
})
when(mockUseTrackProtocolRunEvent)
.calledWith(RUN_ID, ROBOT_NAME)
.mockReturnValue({
trackProtocolRunEvent: mockTrackProtocolRunEvent,
})
when(mockUseRunControls)
.calledWith(RUN_ID, expect.anything())
.mockReturnValue({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ let store: Store<any> = createStore(jest.fn(), {})

const RUN_ID = '1'
const RUN_ID_2 = '2'
const ROBOT_NAME = 'otie'

const PIPETTES = [
{ id: '1', pipetteName: 'testModelLeft' },
Expand Down Expand Up @@ -111,9 +112,12 @@ describe('useProtocolAnalysisErrors hook', () => {
})

it('returns getProtocolRunAnalyticsData function', () => {
const { result } = renderHook(() => useProtocolRunAnalyticsData(RUN_ID), {
wrapper,
})
const { result } = renderHook(
() => useProtocolRunAnalyticsData(RUN_ID, ROBOT_NAME),
{
wrapper,
}
)
expect(typeof result.current.getProtocolRunAnalyticsData).toEqual(
'function'
)
Expand All @@ -123,9 +127,12 @@ describe('useProtocolAnalysisErrors hook', () => {
when(mockUseProtocolDetailsForRun)
.calledWith(RUN_ID_2)
.mockReturnValue({ protocolData: ROBOT_PROTOCOL_ANALYSIS } as any)
const { result } = renderHook(() => useProtocolRunAnalyticsData(RUN_ID_2), {
wrapper,
})
const { result } = renderHook(
() => useProtocolRunAnalyticsData(RUN_ID_2, ROBOT_NAME),
{
wrapper,
}
)
const protocolRunAnalyticsData = await waitFor(() =>
result.current.getProtocolRunAnalyticsData()
)
Expand All @@ -142,15 +149,19 @@ describe('useProtocolAnalysisErrors hook', () => {
protocolText: 'hashedString',
protocolType: '',
robotType: 'OT-2 Standard',
robotSerialNumber: '',
},
runTime: '1:00:00',
})
})

it('getProtocolRunAnalyticsData returns fallback stored data when robot data unavailable', async () => {
const { result } = renderHook(() => useProtocolRunAnalyticsData(RUN_ID), {
wrapper,
})
const { result } = renderHook(
() => useProtocolRunAnalyticsData(RUN_ID, ROBOT_NAME),
{
wrapper,
}
)
const protocolRunAnalyticsData = await waitFor(() =>
result.current.getProtocolRunAnalyticsData()
)
Expand All @@ -167,6 +178,7 @@ describe('useProtocolAnalysisErrors hook', () => {
protocolText: 'hashedString',
protocolType: 'json',
robotType: 'OT-2 Standard',
robotSerialNumber: '',
},
runTime: '1:00:00',
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import { useRobot } from '../'
import { useRobotAnalyticsData } from '../useRobotAnalyticsData'
import { getAttachedPipettes } from '../../../../redux/pipettes'
import { getRobotSettings } from '../../../../redux/robot-settings'
import { mockConnectableRobot } from '../../../../redux/discovery/__fixtures__'

import {
getRobotApiVersion,
getRobotFirmwareVersion,
getRobotSerialNumber,
} from '../../../../redux/discovery'

import type { DiscoveredRobot } from '../../../../redux/discovery/types'
Expand All @@ -36,6 +39,9 @@ const mockGetRobotFirmwareVersion = getRobotFirmwareVersion as jest.MockedFuncti
const mockGetAttachedPipettes = getAttachedPipettes as jest.MockedFunction<
typeof getAttachedPipettes
>
const mockGetRobotSerialNumber = getRobotSerialNumber as jest.MockedFunction<
typeof getRobotSerialNumber
>

const ROBOT_SETTINGS = [
{ id: `setting1`, value: true, title: '', description: '' },
Expand All @@ -47,6 +53,7 @@ const ATTACHED_PIPETTES = {
left: { id: '1', model: 'testModelLeft' },
right: { id: '2', model: 'testModelRight' },
}
const ROBOT_SERIAL_NUMBER = 'OT123'

let wrapper: React.FunctionComponent<{ children: React.ReactNode }>
let store: Store<any> = createStore(jest.fn(), {})
Expand All @@ -70,6 +77,7 @@ describe('useProtocolAnalysisErrors hook', () => {
mockGetAttachedPipettes.mockReturnValue(
ATTACHED_PIPETTES as AttachedPipettesByMount
)
mockGetRobotSerialNumber.mockReturnValue(ROBOT_SERIAL_NUMBER)
})

afterEach(() => {
Expand All @@ -87,7 +95,13 @@ describe('useProtocolAnalysisErrors hook', () => {
it('returns robot analytics data when robot exists', () => {
when(mockUseRobot)
.calledWith('otie')
.mockReturnValue({} as DiscoveredRobot)
.mockReturnValue({
...mockConnectableRobot,
health: {
...mockConnectableRobot.health,
robot_serial: ROBOT_SERIAL_NUMBER,
},
} as DiscoveredRobot)

const { result } = renderHook(() => useRobotAnalyticsData('otie'), {
wrapper,
Expand All @@ -99,6 +113,7 @@ describe('useProtocolAnalysisErrors hook', () => {
robotLeftPipette: 'testModelLeft',
robotRightPipette: 'testModelRight',
robotSmoothieVersion: ROBOT_FIRMWARE_VERSION,
robotSerialNumber: ROBOT_SERIAL_NUMBER,
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ describe('useTrackCreateProtocolRunEvent hook', () => {

it('returns trackCreateProtocolRunEvent function', () => {
const { result } = renderHook(
() => useTrackCreateProtocolRunEvent(storedProtocolData),
() => useTrackCreateProtocolRunEvent(storedProtocolData, 'otie'),
{
wrapper,
}
Expand All @@ -82,7 +82,7 @@ describe('useTrackCreateProtocolRunEvent hook', () => {

it('trackCreateProtocolRunEvent invokes trackEvent with correct props', async () => {
const { result } = renderHook(
() => useTrackCreateProtocolRunEvent(storedProtocolData),
() => useTrackCreateProtocolRunEvent(storedProtocolData, 'otie'),
{
wrapper,
}
Expand All @@ -107,7 +107,7 @@ describe('useTrackCreateProtocolRunEvent hook', () => {
})
)
const { result } = renderHook(
() => useTrackCreateProtocolRunEvent(storedProtocolData),
() => useTrackCreateProtocolRunEvent(storedProtocolData, 'otie'),
{
wrapper,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const mockUseProtocolRunAnalyticsData = useProtocolRunAnalyticsData as jest.Mock
>

const RUN_ID = 'runId'
const ROBOT_NAME = 'otie'
const PROTOCOL_PROPERTIES = { protocolType: 'python' }

let mockTrackEvent: jest.Mock
Expand Down Expand Up @@ -54,9 +55,11 @@ describe('useTrackProtocolRunEvent hook', () => {
)
)
mockUseTrackEvent.mockReturnValue(mockTrackEvent)
when(mockUseProtocolRunAnalyticsData).calledWith(RUN_ID).mockReturnValue({
getProtocolRunAnalyticsData: mockGetProtocolRunAnalyticsData,
})
when(mockUseProtocolRunAnalyticsData)
.calledWith(RUN_ID, ROBOT_NAME)
.mockReturnValue({
getProtocolRunAnalyticsData: mockGetProtocolRunAnalyticsData,
})
})

afterEach(() => {
Expand All @@ -65,16 +68,22 @@ describe('useTrackProtocolRunEvent hook', () => {
})

it('returns trackProtocolRunEvent function', () => {
const { result } = renderHook(() => useTrackProtocolRunEvent(RUN_ID), {
wrapper,
})
const { result } = renderHook(
() => useTrackProtocolRunEvent(RUN_ID, ROBOT_NAME),
{
wrapper,
}
)
expect(typeof result.current.trackProtocolRunEvent).toBe('function')
})

it('trackProtocolRunEvent invokes trackEvent with correct props', async () => {
const { result } = renderHook(() => useTrackProtocolRunEvent(RUN_ID), {
wrapper,
})
const { result } = renderHook(
() => useTrackProtocolRunEvent(RUN_ID, ROBOT_NAME),
{
wrapper,
}
)
await waitFor(() =>
result.current.trackProtocolRunEvent({
name: ANALYTICS_PROTOCOL_RUN_START,
Expand All @@ -89,16 +98,19 @@ describe('useTrackProtocolRunEvent hook', () => {

it('trackProtocolRunEvent calls trackEvent without props when error is thrown in getProtocolRunAnalyticsData', async () => {
when(mockUseProtocolRunAnalyticsData)
.calledWith('errorId')
.calledWith('errorId', ROBOT_NAME)
.mockReturnValue({
getProtocolRunAnalyticsData: () =>
new Promise(() => {
throw new Error('error')
}),
})
const { result } = renderHook(() => useTrackProtocolRunEvent('errorId'), {
wrapper,
})
const { result } = renderHook(
() => useTrackProtocolRunEvent('errorId', ROBOT_NAME),
{
wrapper,
}
)
await waitFor(() =>
result.current.trackProtocolRunEvent({
name: ANALYTICS_PROTOCOL_RUN_START,
Expand Down
21 changes: 17 additions & 4 deletions app/src/organisms/Devices/hooks/useProtocolRunAnalyticsData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { useSelector } from 'react-redux'

import { hash } from '../../../redux/analytics/hash'
import { getStoredProtocol } from '../../../redux/protocol-storage'
import { useStoredProtocolAnalysis, useProtocolDetailsForRun } from './'
import { getRobotSerialNumber } from '../../../redux/discovery'
import {
useRobot,
useStoredProtocolAnalysis,
useProtocolDetailsForRun,
} from './'
import { useProtocolMetadata } from './useProtocolMetadata'
import { useRunTimestamps } from '../../RunTimeControl/hooks'
import { formatInterval } from '../../RunTimeControl/utils'
Expand All @@ -16,13 +21,18 @@ import type { State } from '../../../redux/types'
export const parseProtocolRunAnalyticsData = (
protocolAnalysis: ProtocolAnalysisOutput | null,
storedProtocol: StoredProtocolData | null,
startedAt: string | null
startedAt: string | null,
robotName: string
) => () => {
const hashTasks = [
hash(protocolAnalysis?.metadata?.author) ?? '',
hash(storedProtocol?.srcFiles?.toString() ?? '') ?? '',
]

const robot = useRobot(robotName)
const serialNumber =
robot?.status != null ? getRobotSerialNumber(robot) : null

return Promise.all(hashTasks).then(([protocolAuthor, protocolText]) => ({
protocolRunAnalyticsData: {
protocolType: protocolAnalysis?.config?.protocolType ?? '',
Expand All @@ -49,6 +59,7 @@ export const parseProtocolRunAnalyticsData = (
protocolAnalysis?.robotType != null
? protocolAnalysis?.robotType
: storedProtocol?.mostRecentAnalysis?.robotType,
robotSerialNumber: serialNumber ?? '',
},
runTime:
startedAt != null ? formatInterval(startedAt, Date()) : EMPTY_TIMESTAMP,
Expand All @@ -68,7 +79,8 @@ type GetProtocolRunAnalyticsData = () => Promise<{
* data properties for use in event trackEvent
*/
export function useProtocolRunAnalyticsData(
runId: string | null
runId: string | null,
robotName: string
): {
getProtocolRunAnalyticsData: GetProtocolRunAnalyticsData
} {
Expand Down Expand Up @@ -96,7 +108,8 @@ export function useProtocolRunAnalyticsData(
const getProtocolRunAnalyticsData = parseProtocolRunAnalyticsData(
protocolAnalysis as ProtocolAnalysisOutput | null,
storedProtocol,
startedAt
startedAt,
robotName
)

return { getProtocolRunAnalyticsData }
Expand Down
Loading

0 comments on commit 0c19564

Please sign in to comment.