Skip to content

Commit

Permalink
protocol upload takes RTP values and sends them to python executor
Browse files Browse the repository at this point in the history
  • Loading branch information
sanni-t committed Mar 18, 2024
1 parent 6695f9e commit e61e161
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 4 deletions.
2 changes: 2 additions & 0 deletions api/src/opentrons/protocol_engine/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -909,3 +909,5 @@ class EnumParameter(RTPBase):


RunTimeParameter = Union[IntParameter, FloatParameter, EnumParameter]

RunTimeParameterValues = Dict[str, Union[float, bool, str]] # update value types as more RTP types are added
13 changes: 11 additions & 2 deletions api/src/opentrons/protocol_reader/protocol_reader.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Read relevant protocol information from a set of files."""
from pathlib import Path
from typing import Optional, Sequence
from typing import Optional, Sequence, Dict, Union

from opentrons.protocols.parse import PythonParseMode

Expand All @@ -24,6 +24,7 @@
JsonProtocolConfig,
PythonProtocolConfig,
)
from ..protocol_engine.types import RunTimeParameterValues


class ProtocolReader:
Expand Down Expand Up @@ -53,7 +54,10 @@ def __init__(
self._file_hasher = file_hasher or FileHasher()

async def save(
self, files: Sequence[BufferedFile], directory: Path, content_hash: str
self,
files: Sequence[BufferedFile],
directory: Path, content_hash: str,
run_time_param_values: RunTimeParameterValues,
) -> ProtocolSource:
"""Compute a `ProtocolSource` from buffered files and save them as files.
Expand All @@ -65,6 +69,7 @@ async def save(
files: List buffered files. Do not attempt to reuse any objects
in this list once they've been passed to the ProtocolReader.
directory: Name of the directory to create and place files in.
run_time_param_values: Client-supplied values for Run Time Parameters.
Returns:
A ProtocolSource describing the validated protocol.
Expand Down Expand Up @@ -98,6 +103,7 @@ async def save(
config=self._map_config(role_analysis),
robot_type=role_analysis.main_file.robot_type,
metadata=role_analysis.main_file.metadata,
run_time_param_values=run_time_param_values,
)

async def read_saved(
Expand Down Expand Up @@ -164,6 +170,9 @@ async def read_saved(
config=self._map_config(role_analysis),
robot_type=role_analysis.main_file.robot_type,
metadata=role_analysis.main_file.metadata,
# We are not passing any RTP values, just reading the existing protocol.
# RTPs passed when the protocol was uploaded/ analyzed will be in the analysis.
run_time_param_values=None,
)

@staticmethod
Expand Down
2 changes: 2 additions & 0 deletions api/src/opentrons/protocol_reader/protocol_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Any, Dict, List, Optional, Union
from typing_extensions import Literal

from opentrons.protocol_engine.types import RunTimeParameterValues
from opentrons.protocols.api_support.types import APIVersion

from opentrons_shared_data.robot.dev_types import RobotType
Expand Down Expand Up @@ -122,3 +123,4 @@ class ProtocolSource:
metadata: Metadata
robot_type: RobotType
config: ProtocolConfig
run_time_param_values: Optional[RunTimeParameterValues]
1 change: 1 addition & 0 deletions api/src/opentrons/protocol_runner/legacy_wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ def read(
extra_data={
data_path.name: data_path.read_bytes() for data_path in data_file_paths
},
run_time_param_values=protocol_source.run_time_param_values,
python_parse_mode=python_parse_mode,
)

Expand Down
5 changes: 5 additions & 0 deletions api/src/opentrons/protocols/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ def _parse_json(
def _parse_python(
protocol_contents: Union[str, bytes],
python_parse_mode: PythonParseMode,
run_time_param_values: Optional[Dict[str, Union[float, bool, str]]],
filename: Optional[str] = None,
bundled_labware: Optional[Dict[str, "LabwareDefinition"]] = None,
bundled_data: Optional[Dict[str, bytes]] = None,
Expand Down Expand Up @@ -273,6 +274,7 @@ def _parse_python(
bundled_data=bundled_data,
bundled_python=bundled_python,
extra_labware=extra_labware,
run_time_param_values=run_time_param_values,
)

return result
Expand Down Expand Up @@ -303,6 +305,7 @@ def _parse_bundle(

def parse(
protocol_file: Union[str, bytes],
run_time_param_values: Optional[Dict[str, Union[float, bool, str]]],
filename: Optional[str] = None,
extra_labware: Optional[Dict[str, "LabwareDefinition"]] = None,
extra_data: Optional[Dict[str, bytes]] = None,
Expand All @@ -314,6 +317,8 @@ def parse(
:param protocol_file: The protocol file, or for single-file protocols, a
string of the protocol contents.
:param run_time_param_values: Values of run-time parameters sent by the client,
to be used during analysis/ run.
:param filename: The name of the protocol. Optional, but helps with
deducing the kind of protocol (e.g. if it ends with
'.json' we can treat it like json)
Expand Down
2 changes: 2 additions & 0 deletions api/src/opentrons/protocols/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ class PythonProtocol(_ProtocolCommon):
bundled_python: Optional[Dict[str, str]]
# this should only be included when the protocol is not a zip
extra_labware: Optional[Dict[str, "LabwareDefinition"]]
# TODO: Use a common type definition between engine's RTP type & this one
run_time_param_values: Optional[Dict[str, Union[float, bool, str]]]


Protocol = Union[JsonProtocol, PythonProtocol]
Expand Down
14 changes: 12 additions & 2 deletions robot-server/robot_server/protocols/router.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Router for /protocols endpoints."""
import json
import logging
from textwrap import dedent
from datetime import datetime
Expand Down Expand Up @@ -165,6 +166,10 @@ async def create_protocol(
" protocol resources on the robot."
),
),
runTimeParameterValues: Optional[str] = Form(
default=None,
description="Key value pairs of run-time parameters defined in a protocol."
),
protocol_directory: Path = Depends(get_protocol_directory),
protocol_store: ProtocolStore = Depends(get_protocol_store),
analysis_store: AnalysisStore = Depends(get_analysis_store),
Expand All @@ -184,6 +189,7 @@ async def create_protocol(
Arguments:
files: List of uploaded files, from form-data.
key: Optional key for client-side tracking
runTimeParameterValues: Key value pairs of run-time parameters defined in a protocol.
protocol_directory: Location to store uploaded files.
protocol_store: In-memory database of protocol resources.
analysis_store: In-memory database of protocol analyses.
Expand All @@ -205,10 +211,12 @@ async def create_protocol(
assert file.filename is not None
buffered_files = await file_reader_writer.read(files=files) # type: ignore[arg-type]

parsed_rtp = json.loads(runTimeParameterValues) if runTimeParameterValues else None
content_hash = await file_hasher.hash(buffered_files)
cached_protocol_id = protocol_store.get_id_by_hash(content_hash)

if cached_protocol_id is not None:
if not parsed_rtp and cached_protocol_id is not None:
# Neither a new protocol nor any run-time parameters passed with an existing protocol.
resource = protocol_store.get(protocol_id=cached_protocol_id)
analyses = analysis_store.get_summaries_by_protocol(
protocol_id=cached_protocol_id
Expand All @@ -228,7 +236,8 @@ async def create_protocol(
)

log.info(
f'Protocol with id "{cached_protocol_id}" with same contents already exists. returning existing protocol data in response payload'
f'Protocol with id "{cached_protocol_id}" with same contents already exists.'
f' Returning existing protocol data in response payload.'
)

return await PydanticResponse.create(
Expand All @@ -243,6 +252,7 @@ async def create_protocol(
files=buffered_files,
directory=protocol_directory / protocol_id,
content_hash=content_hash,
run_time_param_values=parsed_rtp,
)
except ProtocolFilesInvalidError as e:
raise ProtocolFilesInvalid(detail=str(e)).as_error(
Expand Down

0 comments on commit e61e161

Please sign in to comment.