From 487e9b3e6f3fb8aa4c5c50db467eba98d156145b Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Thu, 17 Oct 2024 15:28:20 -0400 Subject: [PATCH 01/27] prepare to aspirate over pressue --- .../commands/prepare_to_aspirate.py | 75 +++++++++++++++---- .../commands/test_prepare_to_aspirate.py | 69 +++++++++++++++-- 2 files changed, 123 insertions(+), 21 deletions(-) diff --git a/api/src/opentrons/protocol_engine/commands/prepare_to_aspirate.py b/api/src/opentrons/protocol_engine/commands/prepare_to_aspirate.py index d427b38dc1e..b7e5fd9b8a5 100644 --- a/api/src/opentrons/protocol_engine/commands/prepare_to_aspirate.py +++ b/api/src/opentrons/protocol_engine/commands/prepare_to_aspirate.py @@ -1,18 +1,28 @@ """Prepare to aspirate command request, result, and implementation models.""" from __future__ import annotations +from opentrons_shared_data.errors.exceptions import PipetteOverpressureError from pydantic import BaseModel -from typing import TYPE_CHECKING, Optional, Type +from typing import TYPE_CHECKING, Optional, Type, Union from typing_extensions import Literal from .pipetting_common import ( + OverpressureError, PipetteIdMixin, ) -from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData +from .command import ( + AbstractCommandImpl, + BaseCommand, + BaseCommandCreate, + DefinedErrorData, + SuccessData, +) from ..errors.error_occurrence import ErrorOccurrence if TYPE_CHECKING: - from ..execution.pipetting import PipettingHandler + from ..execution import PipettingHandler, GantryMover + from ..resources import ModelUtils + PrepareToAspirateCommandType = Literal["prepareToAspirate"] @@ -29,25 +39,60 @@ class PrepareToAspirateResult(BaseModel): pass +_ExecuteReturn = Union[ + SuccessData[PrepareToAspirateResult, None], + DefinedErrorData[OverpressureError], +] + + class PrepareToAspirateImplementation( - AbstractCommandImpl[ - PrepareToAspirateParams, SuccessData[PrepareToAspirateResult, None] - ] + AbstractCommandImpl[PrepareToAspirateParams, _ExecuteReturn] ): """Prepare for aspirate command implementation.""" - def __init__(self, pipetting: PipettingHandler, **kwargs: object) -> None: + def __init__( + self, + pipetting: PipettingHandler, + model_utils: ModelUtils, + gantry_mover: GantryMover, + **kwargs: object, + ) -> None: self._pipetting_handler = pipetting + self._model_utils = model_utils + self._gantry_mover = gantry_mover - async def execute( - self, params: PrepareToAspirateParams - ) -> SuccessData[PrepareToAspirateResult, None]: + async def execute(self, params: PrepareToAspirateParams) -> _ExecuteReturn: """Prepare the pipette to aspirate.""" - await self._pipetting_handler.prepare_for_aspirate( - pipette_id=params.pipetteId, - ) - - return SuccessData(public=PrepareToAspirateResult(), private=None) + try: + current_position = await self._gantry_mover.get_position(params.pipetteId) + await self._pipetting_handler.prepare_for_aspirate( + pipette_id=params.pipetteId, + ) + except PipetteOverpressureError as e: + return DefinedErrorData( + public=OverpressureError( + id=self._model_utils.generate_id(), + createdAt=self._model_utils.get_timestamp(), + wrappedErrors=[ + ErrorOccurrence.from_failed( + id=self._model_utils.generate_id(), + createdAt=self._model_utils.get_timestamp(), + error=e, + ) + ], + errorInfo=( + { + "retryLocation": ( + current_position.x, + current_position.y, + current_position.z, + ) + } + ), + ), + ) + else: + return SuccessData(public=PrepareToAspirateResult(), private=None) class PrepareToAspirate( diff --git a/api/tests/opentrons/protocol_engine/commands/test_prepare_to_aspirate.py b/api/tests/opentrons/protocol_engine/commands/test_prepare_to_aspirate.py index b11254af481..45e8db96837 100644 --- a/api/tests/opentrons/protocol_engine/commands/test_prepare_to_aspirate.py +++ b/api/tests/opentrons/protocol_engine/commands/test_prepare_to_aspirate.py @@ -1,25 +1,41 @@ """Test prepare to aspirate commands.""" - -from decoy import Decoy +from datetime import datetime +from opentrons.types import Point +import pytest +from decoy import Decoy, matchers from opentrons.protocol_engine.execution import ( PipettingHandler, ) -from opentrons.protocol_engine.commands.command import SuccessData +from opentrons.protocol_engine.commands.command import DefinedErrorData, SuccessData from opentrons.protocol_engine.commands.prepare_to_aspirate import ( PrepareToAspirateParams, PrepareToAspirateImplementation, PrepareToAspirateResult, ) +from opentrons.protocol_engine.execution.gantry_mover import GantryMover +from opentrons.protocol_engine.resources.model_utils import ModelUtils +from opentrons.protocol_engine.commands.pipetting_common import OverpressureError +from opentrons_shared_data.errors.exceptions import PipetteOverpressureError + + +@pytest.fixture +def subject( + pipetting: PipettingHandler, + model_utils: ModelUtils, + gantry_mover: GantryMover, +) -> PrepareToAspirateImplementation: + """Get the implementation subject.""" + return PrepareToAspirateImplementation( + pipetting=pipetting, model_utils=model_utils, gantry_mover=gantry_mover + ) async def test_prepare_to_aspirate_implmenetation( - decoy: Decoy, pipetting: PipettingHandler + decoy: Decoy, subject: PrepareToAspirateImplementation, pipetting: PipettingHandler ) -> None: """A PrepareToAspirate command should have an executing implementation.""" - subject = PrepareToAspirateImplementation(pipetting=pipetting) - data = PrepareToAspirateParams(pipetteId="some id") decoy.when(await pipetting.prepare_for_aspirate(pipette_id="some id")).then_return( @@ -28,3 +44,44 @@ async def test_prepare_to_aspirate_implmenetation( result = await subject.execute(data) assert result == SuccessData(public=PrepareToAspirateResult(), private=None) + + +async def test_overpressure_error( + decoy: Decoy, + gantry_mover: GantryMover, + pipetting: PipettingHandler, + subject: PrepareToAspirateImplementation, + model_utils: ModelUtils, +) -> None: + """It should return an overpressure error if the hardware API indicates that.""" + pipette_id = "pipette-id" + + position = Point(x=1, y=2, z=3) + + error_id = "error-id" + error_timestamp = datetime(year=2020, month=1, day=2) + + data = PrepareToAspirateParams( + pipetteId=pipette_id, + ) + + decoy.when( + await pipetting.prepare_for_aspirate( + pipette_id=pipette_id, + ), + ).then_raise(PipetteOverpressureError()) + + decoy.when(model_utils.generate_id()).then_return(error_id) + decoy.when(model_utils.get_timestamp()).then_return(error_timestamp) + decoy.when(await gantry_mover.get_position(pipette_id)).then_return(position) + + result = await subject.execute(data) + + assert result == DefinedErrorData( + public=OverpressureError.construct( + id=error_id, + createdAt=error_timestamp, + wrappedErrors=[matchers.Anything()], + errorInfo={"retryLocation": (position.x, position.y, position.z)}, + ), + ) From 6a9115f3eda2a22ed450260b75254c2896b50fa4 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Fri, 18 Oct 2024 10:38:03 -0400 Subject: [PATCH 02/27] current position outside the try --- .../opentrons/protocol_engine/commands/prepare_to_aspirate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/opentrons/protocol_engine/commands/prepare_to_aspirate.py b/api/src/opentrons/protocol_engine/commands/prepare_to_aspirate.py index b7e5fd9b8a5..d63e42a7f90 100644 --- a/api/src/opentrons/protocol_engine/commands/prepare_to_aspirate.py +++ b/api/src/opentrons/protocol_engine/commands/prepare_to_aspirate.py @@ -63,8 +63,8 @@ def __init__( async def execute(self, params: PrepareToAspirateParams) -> _ExecuteReturn: """Prepare the pipette to aspirate.""" + current_position = await self._gantry_mover.get_position(params.pipetteId) try: - current_position = await self._gantry_mover.get_position(params.pipetteId) await self._pipetting_handler.prepare_for_aspirate( pipette_id=params.pipetteId, ) From a354759796805dfbbd1827a1ecf76d7cb375de85 Mon Sep 17 00:00:00 2001 From: tamarzanzouri Date: Fri, 18 Oct 2024 12:34:59 -0400 Subject: [PATCH 03/27] change path to relative path --- .../Devices/ChangePipette/InstructionStep.tsx | 18 ++++++------------ .../Devices/ChangePipette/LevelPipette.tsx | 7 +------ 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/InstructionStep.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/InstructionStep.tsx index 05d43fdd11c..8dc18c1f7f5 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/InstructionStep.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/InstructionStep.tsx @@ -29,18 +29,12 @@ export function InstructionStep(props: Props): JSX.Element { const display = displayCategory === 'GEN2' - ? new URL( - `/app/assets/images/change-pip/${direction}-${String( - mount - )}-${channelsKey}-GEN2-${diagram}@3x.png`, - import.meta.url - ).href - : new URL( - `/app/assets/images/change-pip/${direction}-${String( - mount - )}-${channelsKey}-${diagram}@3x.png`, - import.meta.url - ).href + ? `../app/assets/images/change-pip/${direction}-${String( + mount + )}-${channelsKey}-GEN2-${diagram}@3x.png` + : `../app/assets/images/change-pip/${direction}-${String( + mount + )}-${channelsKey}-${diagram}@3x.png` return ( diff --git a/app/src/organisms/Desktop/Devices/ChangePipette/LevelPipette.tsx b/app/src/organisms/Desktop/Devices/ChangePipette/LevelPipette.tsx index db49a4d6861..fdb4ee0e31e 100644 --- a/app/src/organisms/Desktop/Devices/ChangePipette/LevelPipette.tsx +++ b/app/src/organisms/Desktop/Devices/ChangePipette/LevelPipette.tsx @@ -39,12 +39,7 @@ export function LevelingVideo(props: { controls={true} > ) From ee6d5340ac172540886ce25918eb2cea6eeb0ade Mon Sep 17 00:00:00 2001 From: Shlok Amin Date: Thu, 17 Oct 2024 13:16:50 -0400 Subject: [PATCH 04/27] fix(labware-library): make labware creator accessible via external links (#16480) Fixes https://opentrons.atlassian.net/browse/RESC-339 --- labware-library/Makefile | 1 - labware-library/create/index.html | 16 +++++ .../cypress/e2e/labware-creator/create.cy.js | 2 +- .../e2e/labware-creator/customTubeRack.cy.js | 2 +- .../e2e/labware-creator/fileImport.cy.js | 2 +- .../e2e/labware-creator/reservoir.cy.js | 2 +- .../cypress/e2e/labware-creator/tipRack.cy.js | 2 +- .../e2e/labware-creator/tubesBlock.cy.js | 4 +- .../e2e/labware-creator/tubesRack.cy.js | 6 +- .../e2e/labware-creator/wellPlate.cy.js | 2 +- labware-library/renderStatic.js | 62 ------------------- .../LabwareList/CustomLabwareCard.tsx | 3 +- .../components/LabwareList/LabwareCard.tsx | 3 +- .../src/components/Nav/Breadcrumbs.tsx | 3 +- .../src/components/Sidebar/LabwareGuide.tsx | 6 +- .../website-navigation/SubdomainNav.tsx | 3 +- labware-library/src/filters.tsx | 3 +- labware-library/src/index.tsx | 11 ++-- .../labware-creator/components/IntroCopy.tsx | 5 +- labware-library/src/public-path.ts | 14 ----- labware-library/vite.config.mts | 7 +++ 21 files changed, 46 insertions(+), 113 deletions(-) create mode 100644 labware-library/create/index.html delete mode 100644 labware-library/renderStatic.js delete mode 100644 labware-library/src/public-path.ts diff --git a/labware-library/Makefile b/labware-library/Makefile index 2ccca5f45a8..a074edd4092 100644 --- a/labware-library/Makefile +++ b/labware-library/Makefile @@ -25,7 +25,6 @@ clean: dist: export NODE_ENV := production dist: vite build - node ./renderStatic.js # development assets server .PHONY: dev diff --git a/labware-library/create/index.html b/labware-library/create/index.html new file mode 100644 index 00000000000..84c7c4cb7d3 --- /dev/null +++ b/labware-library/create/index.html @@ -0,0 +1,16 @@ + + + + + + Redirecting to Labware Creator + + + +

Redirecting to Labware Creator...

+ + \ No newline at end of file diff --git a/labware-library/cypress/e2e/labware-creator/create.cy.js b/labware-library/cypress/e2e/labware-creator/create.cy.js index b81026d003a..299a3444a86 100644 --- a/labware-library/cypress/e2e/labware-creator/create.cy.js +++ b/labware-library/cypress/e2e/labware-creator/create.cy.js @@ -4,7 +4,7 @@ context('The Labware Creator Landing Page', () => { beforeEach(() => { - cy.visit('/create') + cy.visit('/#/create') cy.viewport('macbook-15') }) diff --git a/labware-library/cypress/e2e/labware-creator/customTubeRack.cy.js b/labware-library/cypress/e2e/labware-creator/customTubeRack.cy.js index f3c195030be..319e7f4ea81 100644 --- a/labware-library/cypress/e2e/labware-creator/customTubeRack.cy.js +++ b/labware-library/cypress/e2e/labware-creator/customTubeRack.cy.js @@ -6,7 +6,7 @@ const expectedExportFixture = context('Tubes and Rack', () => { before(() => { - cy.visit('/create') + cy.visit('/#/create') cy.viewport('macbook-15') }) diff --git a/labware-library/cypress/e2e/labware-creator/fileImport.cy.js b/labware-library/cypress/e2e/labware-creator/fileImport.cy.js index 408e6fc1ea2..e0fc480107f 100644 --- a/labware-library/cypress/e2e/labware-creator/fileImport.cy.js +++ b/labware-library/cypress/e2e/labware-creator/fileImport.cy.js @@ -4,7 +4,7 @@ const importedLabwareFile = 'TestLabwareDefinition.json' describe('File Import', () => { before(() => { - cy.visit('/create') + cy.visit('/#/create') cy.viewport('macbook-15') }) diff --git a/labware-library/cypress/e2e/labware-creator/reservoir.cy.js b/labware-library/cypress/e2e/labware-creator/reservoir.cy.js index c88044d1678..75197208859 100644 --- a/labware-library/cypress/e2e/labware-creator/reservoir.cy.js +++ b/labware-library/cypress/e2e/labware-creator/reservoir.cy.js @@ -4,7 +4,7 @@ context('Reservoirs', () => { before(() => { - cy.visit('/create') + cy.visit('/#/create') cy.viewport('macbook-15') }) diff --git a/labware-library/cypress/e2e/labware-creator/tipRack.cy.js b/labware-library/cypress/e2e/labware-creator/tipRack.cy.js index 4d633ffd5f6..e69e3dd7285 100644 --- a/labware-library/cypress/e2e/labware-creator/tipRack.cy.js +++ b/labware-library/cypress/e2e/labware-creator/tipRack.cy.js @@ -5,7 +5,7 @@ const expectedExportFixture = '../fixtures/generic_1_tiprack_20ul.json' describe('Create a Tip Rack', () => { before(() => { - cy.visit('/create') + cy.visit('/#/create') cy.viewport('macbook-15') }) it('Should create a tip rack', () => { diff --git a/labware-library/cypress/e2e/labware-creator/tubesBlock.cy.js b/labware-library/cypress/e2e/labware-creator/tubesBlock.cy.js index b891aedafd2..66ea8d0dedc 100644 --- a/labware-library/cypress/e2e/labware-creator/tubesBlock.cy.js +++ b/labware-library/cypress/e2e/labware-creator/tubesBlock.cy.js @@ -4,7 +4,7 @@ context('Tubes and Block', () => { beforeEach(() => { - cy.visit('/create') + cy.visit('/#/create') cy.viewport('macbook-15') cy.get('label') @@ -445,7 +445,7 @@ context('Tubes and Block', () => { }) it('tests the whole form and file export', () => { - cy.visit('/create') + cy.visit('/#/create') cy.viewport('macbook-15') cy.get('label') .contains('What type of labware are you creating?') diff --git a/labware-library/cypress/e2e/labware-creator/tubesRack.cy.js b/labware-library/cypress/e2e/labware-creator/tubesRack.cy.js index 738124ee2e8..4214f215dc0 100644 --- a/labware-library/cypress/e2e/labware-creator/tubesRack.cy.js +++ b/labware-library/cypress/e2e/labware-creator/tubesRack.cy.js @@ -5,7 +5,7 @@ context('Tubes and Rack', () => { describe('Six tubes', () => { before(() => { - cy.visit('/create') + cy.visit('/#/create') cy.viewport('macbook-15') cy.get('label') .contains('What type of labware are you creating?') @@ -137,7 +137,7 @@ context('Tubes and Rack', () => { describe('Fifteen tubes', () => { before(() => { - cy.visit('/create') + cy.visit('/#/create') cy.viewport('macbook-15') cy.get('label') @@ -268,7 +268,7 @@ context('Tubes and Rack', () => { describe('Twentyfour tubes', () => { before(() => { - cy.visit('/create') + cy.visit('/#/create') cy.viewport('macbook-15') cy.get('label') diff --git a/labware-library/cypress/e2e/labware-creator/wellPlate.cy.js b/labware-library/cypress/e2e/labware-creator/wellPlate.cy.js index d586f8040b6..5b27cfcfd72 100644 --- a/labware-library/cypress/e2e/labware-creator/wellPlate.cy.js +++ b/labware-library/cypress/e2e/labware-creator/wellPlate.cy.js @@ -8,7 +8,7 @@ context('Well Plates', () => { before(() => { - cy.visit('/create') + cy.visit('/#/create') cy.viewport('macbook-15') }) diff --git a/labware-library/renderStatic.js b/labware-library/renderStatic.js deleted file mode 100644 index b3fa4262d3c..00000000000 --- a/labware-library/renderStatic.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict' -// Use react-snap to crawl from the specified URL paths and prerender HTML for those pages. -// Since paths to JS/CSS assets are relative to webpack publicPath, and not relative to -// the location of the page being prerendered, those src/href paths need to be prefixed with -// the correct number of `../`'s to reference the project root. -// -// For example, the output of react-snap for a page at http://localhost:PORT/path/to/page/ -// will have a