Skip to content

Commit

Permalink
fix(app): clone run with RTPs from HistoricalProtocolRun
Browse files Browse the repository at this point in the history
Here, I extend the useCloneRun hook to optionally create a new analysis for a protocol with RTP
override values that will be referenced at protocol setup. This provides functionality for cloning a
HistoricalProtocolRun with its RTP values preserved.
  • Loading branch information
ncdiehl committed Apr 18, 2024
1 parent 585f69e commit 7e1ccb8
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 33 deletions.
11 changes: 11 additions & 0 deletions api-client/src/runs/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {
RUN_STATUS_FAILED,
RUN_STATUS_STOPPED,
RUN_STATUS_SUCCEEDED,
} from './types'

export const RUN_STATUSES_TERMINAL = [
RUN_STATUS_SUCCEEDED,
RUN_STATUS_FAILED,
RUN_STATUS_STOPPED,
]
2 changes: 1 addition & 1 deletion api-client/src/runs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ export { getCommands } from './commands/getCommands'
export { createRunAction } from './createRunAction'
export * from './createLabwareOffset'
export * from './createLabwareDefinition'

export * from './constants'
export * from './types'
export type { CreateRunData } from './createRun'
3 changes: 2 additions & 1 deletion api-client/src/runs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
LoadedPipette,
ModuleModel,
RunTimeCommand,
RunTimeParameter,
} from '@opentrons/shared-data'
import type { ResourceLink, ErrorDetails } from '../types'
export * from './commands/types'
Expand Down Expand Up @@ -47,7 +48,7 @@ export interface LegacyGoodRunData {
modules: LoadedModule[]
protocolId?: string
labwareOffsets?: LabwareOffset[]
runTimeParameterValues?: RunTimeParameterCreateData
runTimeParameters: RunTimeParameter[]
}

export interface KnownGoodRunData extends LegacyGoodRunData {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import styled, { css } from 'styled-components'
import {
RUN_STATUS_STOPPED,
RUN_STATUSES_TERMINAL,
} from '@opentrons/api-client'
import { formatRunTimeParameterValue } from '@opentrons/shared-data'
import {
ALIGN_CENTER,
Expand All @@ -25,6 +29,8 @@ import { Tooltip } from '../../../atoms/Tooltip'
import { useMostRecentCompletedAnalysis } from '../../LabwarePositionCheck/useMostRecentCompletedAnalysis'

import type { RunTimeParameter } from '@opentrons/shared-data'
import { useRunStatus } from '../../RunTimeControl/hooks'
import { useNotifyRunQuery } from '../../../resources/runs'

interface ProtocolRunRuntimeParametersProps {
runId: string
Expand All @@ -34,7 +40,15 @@ export function ProtocolRunRuntimeParameters({
}: ProtocolRunRuntimeParametersProps): JSX.Element {
const { t } = useTranslation('protocol_setup')
const mostRecentAnalysis = useMostRecentCompletedAnalysis(runId)
const runTimeParameters = mostRecentAnalysis?.runTimeParameters ?? []
const runStatus = useRunStatus(runId)
const isRunTerminal =
// @ts-expect-error: expected that runStatus may not be a terminal status
runStatus == null ? false : RUN_STATUSES_TERMINAL.includes(runStatus)
const run = useNotifyRunQuery(runId).data
const runTimeParameters =
(isRunTerminal
? run?.data?.runTimeParameters
: mostRecentAnalysis?.runTimeParameters) ?? []
const hasParameter = runTimeParameters.length > 0

const hasCustomValues = runTimeParameters.some(
Expand Down Expand Up @@ -79,7 +93,11 @@ export function ProtocolRunRuntimeParameters({
</Flex>
{!hasParameter ? (
<Flex padding={SPACING.spacing16}>
<InfoScreen contentType="parameters" />
<InfoScreen
contentType={
runStatus === RUN_STATUS_STOPPED ? 'runNotStarted' : 'parameters'
}
/>
</Flex>
) : (
<>
Expand Down
1 change: 1 addition & 0 deletions app/src/organisms/InterventionModal/__fixtures__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export const mockRunData: RunData = {
pipettes: [],
labware: [mockLabwareOnModule, mockLabwareOnSlot, mockLabwareOffDeck],
modules: [mockModule],
runTimeParameters: [],
}

export const mockLabwareRenderInfo = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const mockRun = {
pipettes: [],
protocolId: 'mockSortedProtocolID',
status: 'stopped',
runTimeParameters: [],
}

const render = (
Expand Down
48 changes: 34 additions & 14 deletions app/src/organisms/ProtocolUpload/hooks/useCloneRun.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { useQueryClient } from 'react-query'

import { useHost, useCreateRunMutation } from '@opentrons/react-api-client'

import {
useHost,
useCreateRunMutation,
useCreateProtocolAnalysisMutation,
} from '@opentrons/react-api-client'
import { useNotifyRunQuery } from '../../../resources/runs'

import type { Run } from '@opentrons/api-client'
import type { Run, RunTimeParameterCreateData } from '@opentrons/api-client'

interface UseCloneRunResult {
cloneRun: () => void
Expand All @@ -13,28 +16,45 @@ interface UseCloneRunResult {

export function useCloneRun(
runId: string | null,
onSuccessCallback?: (createRunResponse: Run) => unknown
onSuccessCallback?: (createRunResponse: Run) => unknown,
triggerAnalysis: boolean = false
): UseCloneRunResult {
const host = useHost()
const queryClient = useQueryClient()
const { data: runRecord } = useNotifyRunQuery(runId)
const protocolKey = runRecord?.data.protocolId ?? null

const { createRun, isLoading } = useCreateRunMutation({
onSuccess: response => {
queryClient
.invalidateQueries([host, 'runs'])
.catch((e: Error) =>
console.error(`error invalidating runs query: ${e.message}`)
)
const invalidateRuns = queryClient.invalidateQueries([host, 'runs'])
const invalidateProtocols = queryClient.invalidateQueries([
host,
'protocols',
protocolKey,
])
Promise.all([invalidateRuns, invalidateProtocols]).catch((e: Error) =>
console.error(`error invalidating runs query: ${e.message}`)
)
if (onSuccessCallback != null) onSuccessCallback(response)
},
})
const { createProtocolAnalysis } = useCreateProtocolAnalysisMutation(
protocolKey,
host
)
const cloneRun = (): void => {
if (runRecord != null) {
const {
protocolId,
labwareOffsets,
runTimeParameterValues,
} = runRecord.data
const { protocolId, labwareOffsets, runTimeParameters } = runRecord.data
const runTimeParameterValues = runTimeParameters.reduce<RunTimeParameterCreateData>(
(acc, param) =>
param.value !== param.default
? { ...acc, [param.variableName]: param.value }
: acc,
{}
)
if (triggerAnalysis && protocolKey != null) {
createProtocolAnalysis({ protocolKey, runTimeParameterValues })
}
createRun({ protocolId, labwareOffsets, runTimeParameterValues })
} else {
console.info('failed to clone run record, source run record not found')
Expand Down
21 changes: 17 additions & 4 deletions app/src/organisms/RunPreview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { css } from 'styled-components'
import { useTranslation } from 'react-i18next'
import { ViewportList, ViewportListRef } from 'react-viewport-list'

import { RUN_STATUSES_TERMINAL } from '@opentrons/api-client'
import { useAllCommandsQuery } from '@opentrons/react-api-client'
import {
ALIGN_CENTER,
BORDERS,
Expand All @@ -24,6 +26,8 @@ import { CommandText } from '../CommandText'
import { Divider } from '../../atoms/structure'
import { NAV_BAR_WIDTH } from '../../App/constants'
import { CommandIcon } from './CommandIcon'
import { useRunStatus } from '../RunTimeControl/hooks'

import type { RobotType } from '@opentrons/shared-data'

const COLOR_FADE_MS = 500
Expand All @@ -41,6 +45,13 @@ export const RunPreviewComponent = (
): JSX.Element | null => {
const { t } = useTranslation('run_details')
const robotSideAnalysis = useMostRecentCompletedAnalysis(runId)
const runStatus = useRunStatus(runId)
// @ts-expect-error: expected that runStatus may not be a terminal status
const isRunTerminal = RUN_STATUSES_TERMINAL.includes(runStatus)
const commandsFromQuery = useAllCommandsQuery(runId, null, {
staleTime: Infinity,
cacheTime: Infinity,
}).data?.data
const viewPortRef = React.useRef<HTMLDivElement | null>(null)
const currentRunCommandKey = useNotifyLastRunCommandKey(runId, {
refetchInterval: LIVE_RUN_COMMANDS_POLL_MS,
Expand All @@ -50,7 +61,9 @@ export const RunPreviewComponent = (
setIsCurrentCommandVisible,
] = React.useState<boolean>(true)
if (robotSideAnalysis == null) return null
const currentRunCommandIndex = robotSideAnalysis.commands.findIndex(
const commands =
(isRunTerminal ? commandsFromQuery : robotSideAnalysis.commands) ?? []
const currentRunCommandIndex = commands.findIndex(
c => c.key === currentRunCommandKey
)

Expand All @@ -69,7 +82,7 @@ export const RunPreviewComponent = (
{t('run_preview')}
</StyledText>
<StyledText as="label" color={COLORS.grey50}>
{t('steps_total', { count: robotSideAnalysis.commands.length })}
{t('steps_total', { count: commands.length })}
</StyledText>
</Flex>
<StyledText as="p" marginBottom={SPACING.spacing8}>
Expand All @@ -79,7 +92,7 @@ export const RunPreviewComponent = (
<ViewportList
viewportRef={viewPortRef}
ref={ref}
items={robotSideAnalysis.commands}
items={commands}
onViewportIndexesChange={([
lowestVisibleIndex,
highestVisibleIndex,
Expand Down Expand Up @@ -152,7 +165,7 @@ export const RunPreviewComponent = (
{t('view_current_step')}
</PrimaryButton>
) : null}
{currentRunCommandIndex === robotSideAnalysis.commands.length - 1 ? (
{currentRunCommandIndex === commands.length - 1 ? (
<StyledText as="h6" color={COLORS.grey60}>
{t('end_of_protocol')}
</StyledText>
Expand Down
9 changes: 9 additions & 0 deletions app/src/organisms/RunTimeControl/__fixtures__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const mockPausedRun: RunData = {
pipettes: [],
labware: [],
modules: [],
runTimeParameters: [],
}

export const mockPauseRequestedRun: RunData = {
Expand All @@ -65,6 +66,7 @@ export const mockPauseRequestedRun: RunData = {
pipettes: [],
labware: [],
modules: [],
runTimeParameters: [],
}

export const mockRunningRun: RunData = {
Expand Down Expand Up @@ -94,6 +96,7 @@ export const mockRunningRun: RunData = {
pipettes: [],
labware: [],
modules: [],
runTimeParameters: [],
}

export const mockFailedRun: RunData = {
Expand Down Expand Up @@ -133,6 +136,7 @@ export const mockFailedRun: RunData = {
pipettes: [],
labware: [],
modules: [],
runTimeParameters: [],
}

export const mockStopRequestedRun: RunData = {
Expand Down Expand Up @@ -167,6 +171,7 @@ export const mockStopRequestedRun: RunData = {
pipettes: [],
labware: [],
modules: [],
runTimeParameters: [],
}

export const mockStoppedRun: RunData = {
Expand Down Expand Up @@ -201,6 +206,7 @@ export const mockStoppedRun: RunData = {
pipettes: [],
labware: [],
modules: [],
runTimeParameters: [],
}

export const mockSucceededRun: RunData = {
Expand Down Expand Up @@ -230,6 +236,7 @@ export const mockSucceededRun: RunData = {
pipettes: [],
labware: [],
modules: [],
runTimeParameters: [],
}

export const mockIdleUnstartedRun: RunData = {
Expand All @@ -243,6 +250,7 @@ export const mockIdleUnstartedRun: RunData = {
pipettes: [],
labware: [],
modules: [],
runTimeParameters: [],
}

export const mockIdleStartedRun: RunData = {
Expand Down Expand Up @@ -272,6 +280,7 @@ export const mockIdleStartedRun: RunData = {
pipettes: [],
labware: [],
modules: [],
runTimeParameters: [],
}

export const mockCommand = {
Expand Down
3 changes: 2 additions & 1 deletion app/src/organisms/RunTimeControl/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export function useRunControls(

const { cloneRun, isLoading: isResetRunLoading } = useCloneRun(
runId ?? null,
onCloneRunSuccess
onCloneRunSuccess,
true
)

return {
Expand Down
21 changes: 16 additions & 5 deletions components/src/molecules/ParametersTable/InfoScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,25 @@ import { Flex } from '../../primitives'
import { ALIGN_CENTER, DIRECTION_COLUMN } from '../../styles'

interface InfoScreenProps {
contentType: 'parameters' | 'moduleControls'
contentType: 'parameters' | 'moduleControls' | 'runNotStarted'
}

export function InfoScreen({ contentType }: InfoScreenProps): JSX.Element {
const bodyText =
contentType === 'parameters'
? 'No parameters specified in this protocol'
: 'Connect modules to see controls'
let bodyText: string = ''
switch (contentType) {
case 'parameters':
bodyText = 'No parameters specified in this protocol'
break
case 'moduleControls':
bodyText = 'Connect modules to see controls'
break
case 'runNotStarted':
bodyText = 'Run was never started'
break
default:
bodyText = contentType
}

return (
<Flex
alignItems={ALIGN_CENTER}
Expand Down
2 changes: 2 additions & 0 deletions react-api-client/src/runs/__fixtures__/runs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const mockPausedRun: RunData = {
pipettes: [],
labware: [],
modules: [],
runTimeParameters: [],
}

export const mockRunningRun: RunData = {
Expand Down Expand Up @@ -61,6 +62,7 @@ export const mockRunningRun: RunData = {
pipettes: [],
labware: [],
modules: [],
runTimeParameters: [],
}

export const mockRunResponse: Run = {
Expand Down
Loading

0 comments on commit 7e1ccb8

Please sign in to comment.