Skip to content

Commit

Permalink
Merge branch 'edge' into chore_update-vitest-and-related-packages
Browse files Browse the repository at this point in the history
  • Loading branch information
koji committed Aug 20, 2024
2 parents 5cdda2d + 4b4f541 commit 4242c42
Show file tree
Hide file tree
Showing 817 changed files with 21,912 additions and 7,507 deletions.
1 change: 1 addition & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module.exports = {
stories: [
'../components/**/*.stories.@(js|jsx|ts|tsx)',
'../app/**/*.stories.@(js|jsx|ts|tsx)',
'../protocol-designer/**/*.stories.@(js|jsx|ts|tsx)',
'../opentrons-ai-client/**/*.stories.@(js|jsx|ts|tsx)',
],

Expand Down
21 changes: 14 additions & 7 deletions .storybook/preview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ global.APP_SHELL_REMOTE = {
ipcRenderer: {
on: (topic, cb) => {},
invoke: (callname, args) => {},
send: (message, payload) => {}
send: (message, payload) => {},
},
}
global._PKG_VERSION_ = '0.0.0-storybook'
Expand All @@ -29,8 +29,8 @@ export const customViewports = {
type: 'desktop',
styles: {
width: '600px',
height: '450px'
}
height: '450px',
},
},
desktopSmall: {
// A size typically used in figma app backgrounds, useful for viewing
Expand All @@ -39,9 +39,9 @@ export const customViewports = {
type: 'desktop',
styles: {
width: '1024px',
height: '700px'
}
}
height: '700px',
},
},
}

export const parameters = {
Expand All @@ -50,7 +50,14 @@ export const parameters = {
options: {
storySort: {
method: 'alphabetical',
order: ['Design Tokens', 'Library', 'App', 'ODD', 'AI'],
order: [
'Design Tokens',
'Library',
'App',
'ODD',
'Protocol-Designer',
'AI',
],
},
},
}
Expand Down
2 changes: 1 addition & 1 deletion abr-testing/abr_testing/automation/google_sheets_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def open_worksheet(self, tab_number: int) -> Any:
def create_worksheet(self, title: str) -> Optional[str]:
"""Create a worksheet with tab name. Existing spreadsheet needed."""
try:
new_sheet = self.spread_sheet.add_worksheet(title, rows="2500", cols="40")
new_sheet = self.spread_sheet.add_worksheet(title, rows="2500", cols="60")
return new_sheet.id
except gspread.exceptions.APIError:
print("Sheet already exists.")
Expand Down
42 changes: 34 additions & 8 deletions abr-testing/abr_testing/automation/jira_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,17 +170,20 @@ def create_ticket(

def post_attachment_to_ticket(self, issue_id: str, attachment_path: str) -> None:
"""Adds attachments to ticket."""
# TODO: Ensure that file is actually uploaded.
file = {"file": open(attachment_path, "rb")}
JSON_headers = {"Accept": "application/json"}
file = {
"file": (attachment_path, open(attachment_path, "rb"), "application-type")
}
JSON_headers = {"Accept": "application/json", "X-Atlassian-Token": "no-check"}
attachment_url = f"{self.url}/rest/api/3/issue/{issue_id}/attachments"
try:
response = requests.post(
f"{self.url}/rest/api/3/issue/{issue_id}/attachments",
response = requests.request(
"POST",
attachment_url,
headers=JSON_headers,
auth=self.auth,
files=file,
)
print(response)
print(f"File: {attachment_path} posted to ticket {issue_id}.")
except json.JSONDecodeError:
error_message = str(response.content)
print(f"JSON decoding error occurred. Response content: {error_message}.")
Expand Down Expand Up @@ -256,9 +259,9 @@ def get_project_components(self, project_id: str) -> List[Dict[str, str]]:
components_list = response.json()
return components_list

def comment(self, content_list: List[Dict[str, Any]], issue_url: str) -> None:
def comment(self, content_list: List[Dict[str, Any]], issue_key: str) -> None:
"""Leave comment on JIRA Ticket."""
comment_url = issue_url + "/comment"
comment_url = f"{self.url}/rest/api/3/issue/{issue_key}/comment"
payload = json.dumps(
{
"body": {
Expand All @@ -272,6 +275,29 @@ def comment(self, content_list: List[Dict[str, Any]], issue_url: str) -> None:
"POST", comment_url, data=payload, headers=self.headers, auth=self.auth
)

def format_jira_comment(self, comment_info: Any) -> List[Dict[str, Any]]:
"""Formats a string input to work with the "comment" function."""
content_list: List = []
line_1 = {
"type": "paragraph",
"content": [{"type": "text", "text": comment_info}],
}
content_list.insert(0, line_1)
return content_list

def get_ticket(self) -> str:
"""Gets and confirms jira ticket number."""
while True:
issue_key = input("Ticket Key: ")
url = f"{self.url}/rest/api/3/issue/{issue_key}"
headers = {"Accept": "application/json"}
response = requests.request("GET", url, headers=headers, auth=self.auth)
if str(response) == "<Response [200]>":
break
else:
print("Please input a valid JIRA Key")
return issue_key


if __name__ == "__main__":
"""Create ticket for specified robot."""
Expand Down
51 changes: 29 additions & 22 deletions abr-testing/abr_testing/data_collection/abr_robot_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def read_each_log(folder_path: str, issue_url: str) -> None:
"content": [{"type": "text", "text": message}],
}
content_list.insert(0, line_1)
ticket.comment(content_list, issue_url)
ticket.comment(content_list, issue_key)
no_word_found_message = (
f"Key words '{not_found_words} were not found in {file_name}."
)
Expand All @@ -209,7 +209,7 @@ def read_each_log(folder_path: str, issue_url: str) -> None:
"content": [{"type": "text", "text": no_word_found_message}],
}
content_list.append(no_word_found_dict)
ticket.comment(content_list, issue_url)
ticket.comment(content_list, issue_key)


def match_error_to_component(
Expand Down Expand Up @@ -383,21 +383,26 @@ def get_run_error_info_from_robot(
errored_labware_dict["Slot"] = labware["location"].get("slotName", "")
errored_labware_dict["Labware Type"] = labware.get("definitionUri", "")
offset_id = labware.get("offsetId", "")
for lpc in lpc_dict:
if lpc.get("id", "") == offset_id:
errored_labware_dict["X"] = lpc["vector"].get("x", "")
errored_labware_dict["Y"] = lpc["vector"].get("y", "")
errored_labware_dict["Z"] = lpc["vector"].get("z", "")
errored_labware_dict["Module"] = lpc["location"].get(
"moduleModel", ""
)
errored_labware_dict["Adapter"] = lpc["location"].get(
"definitionUri", ""
)

lpc_message = compare_lpc_to_historical_data(
errored_labware_dict, parent, storage_directory
)
if offset_id == "":
labware_slot = errored_labware_dict["Slot"]
lpc_message = f"The current LPC coords found at {labware_slot} are (0, 0, 0). \
Please confirm with the ABR-LPC sheet and re-LPC."
else:
for lpc in lpc_dict:
if lpc.get("id", "") == offset_id:
errored_labware_dict["X"] = lpc["vector"].get("x", "")
errored_labware_dict["Y"] = lpc["vector"].get("y", "")
errored_labware_dict["Z"] = lpc["vector"].get("z", "")
errored_labware_dict["Module"] = lpc["location"].get(
"moduleModel", ""
)
errored_labware_dict["Adapter"] = lpc["location"].get(
"definitionUri", ""
)

lpc_message = compare_lpc_to_historical_data(
errored_labware_dict, parent, storage_directory
)

description["protocol_step"] = protocol_step
description["right_mount"] = results.get("right", "No attachment")
Expand Down Expand Up @@ -535,15 +540,12 @@ def get_run_error_info_from_robot(
affects_version,
parent_key,
)

# Link Tickets
to_link = ticket.match_issues(all_issues, summary)
ticket.link_issues(to_link, issue_key)

# OPEN TICKET
issue_url = ticket.open_issue(issue_key)
# MOVE FILES TO ERROR FOLDER.

error_files = [saved_file_path_calibration, run_log_file_path] + file_paths
error_folder_path = os.path.join(storage_directory, issue_key)
os.makedirs(error_folder_path, exist_ok=True)
Expand All @@ -555,8 +557,11 @@ def get_run_error_info_from_robot(
shutil.move(source_file, destination_file)
except shutil.Error:
continue
# OPEN FOLDER DIRECTORY
subprocess.Popen(["explorer", error_folder_path])
# POST FILES TO TICKET
list_of_files = os.listdir(error_folder_path)
for file in list_of_files:
file_to_attach = os.path.join(error_folder_path, file)
ticket.post_attachment_to_ticket(issue_key, file_to_attach)
# ADD ERROR COMMENTS TO TICKET
read_each_log(error_folder_path, raw_issue_url)
# WRITE ERRORED RUN TO GOOGLE SHEET
Expand Down Expand Up @@ -601,3 +606,5 @@ def get_run_error_info_from_robot(
google_sheet_lpc.batch_update_cells(runs_and_lpc, "A", start_row_lpc, "0")
else:
print("Ticket created.")
# Open folder directory incase uploads to ticket were incomplete
subprocess.Popen(["explorer", error_folder_path])
12 changes: 12 additions & 0 deletions api-client/src/dataFiles/getCsvFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { GET, request } from '../request'

import type { CsvFileDataResponse } from './types'
import type { ResponsePromise } from '../request'
import type { HostConfig } from '../types'

export function getCsvFile(
config: HostConfig,
fileId: string
): ResponsePromise<CsvFileDataResponse> {
return request<CsvFileDataResponse>(GET, `/dataFiles/${fileId}`, null, config)
}
1 change: 1 addition & 0 deletions api-client/src/dataFiles/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { getCsvFile } from './getCsvFile'
export { getCsvFileRaw } from './getCsvFileRaw'
export { uploadCsvFile } from './uploadCsvFile'

Expand Down
4 changes: 3 additions & 1 deletion api-client/src/dataFiles/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ export interface CsvFileData {
name: string
}

export interface UploadedCsvFileResponse {
export interface CsvFileDataResponse {
data: CsvFileData
}

export type UploadedCsvFileResponse = CsvFileDataResponse

export interface UploadedCsvFilesResponse {
data: CsvFileData[]
}
Expand Down
2 changes: 0 additions & 2 deletions api-client/src/protocols/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,3 @@ export { getProtocolIds } from './getProtocolIds'
export { getProtocols } from './getProtocols'

export * from './types'
export * from './utils'
export * from './__fixtures__'
19 changes: 19 additions & 0 deletions api-client/src/runs/commands/getRunCommandErrors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { GET, request } from '../../request'

import type { ResponsePromise } from '../../request'
import type { HostConfig } from '../../types'
import type { GetCommandsParams, RunCommandErrors } from '../types'

export function getRunCommandErrors(
config: HostConfig,
runId: string,
params: GetCommandsParams
): ResponsePromise<RunCommandErrors> {
return request<RunCommandErrors>(
GET,
`/runs/${runId}/commandErrors`,
null,
config,
params
)
}
7 changes: 6 additions & 1 deletion api-client/src/runs/commands/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import type { RunTimeCommand } from '@opentrons/shared-data'
import type { RunTimeCommand, RunCommandError } from '@opentrons/shared-data'

export interface GetCommandsParams {
cursor: number | null // the index of the command at the center of the window
pageLength: number // the number of items to include
}

export interface RunCommandErrors {
data: RunCommandError[]
meta: GetCommandsParams & { totalLength: number }
}

// NOTE: this incantation allows us to omit a key from each item in a union distributively
// this means we can, for example, maintain the associated commandType and params after the Omit is applied
type DistributiveOmit<T, K extends keyof T> = T extends any ? Omit<T, K> : never
Expand Down
1 change: 1 addition & 0 deletions api-client/src/runs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export { getCommand } from './commands/getCommand'
export { getCommands } from './commands/getCommands'
export { getCommandsAsPreSerializedList } from './commands/getCommandsAsPreSerializedList'
export { createRunAction } from './createRunAction'
export { getRunCommandErrors } from './commands/getRunCommandErrors'
export * from './createLabwareOffset'
export * from './createLabwareDefinition'
export * from './constants'
Expand Down
2 changes: 1 addition & 1 deletion api-client/src/runs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ export interface LegacyGoodRunData {
modules: LoadedModule[]
protocolId?: string
labwareOffsets?: LabwareOffset[]
runTimeParameters: RunTimeParameter[]
}

export interface KnownGoodRunData extends LegacyGoodRunData {
ok: true
runTimeParameters: RunTimeParameter[]
}

export interface KnownInvalidRunData extends LegacyGoodRunData {
Expand Down
35 changes: 35 additions & 0 deletions api/docs/v2/basic_commands/liquids.rst
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,38 @@ This example aspirates enough air to fill the remaining volume in a pipette::

.. versionadded:: 2.0

.. _detect-liquid-presence:

Detect Liquids
==============

The :py:meth:`.InstrumentContext.detect_liquid_presence` method tells a Flex pipette to check for liquid in a well. It returns ``True`` if the pressure sensors in the pipette detect a liquid and ``False`` if the sensors do not.

Aspiration isn't required to use ``detect_liquid_presence()``. This is a standalone method that can be called when you want the robot to record the presence or absence of a liquid. When ``detect_liquid_presence()`` finds an empty well it won't raise an error or stop your protocol.

A potential use of liquid detection is to try aspirating from another well if the first well is found to contain no liquid.

.. code-block:: python
if pipette.detect_liquid_presence(reservoir["A1"]):
pipette.aspirate(100, reservoir["A1"])
else:
pipette.aspirate(100, reservoir["A2"])
.. versionadded:: 2.20

.. _require-liquid-presence:

Require Liquids
===============

The :py:meth:`.InstrumentContext.require_liquid_presence` method tells a Flex pipette to check for `and require` liquid in a well.

Aspiration isn't required to use ``require_liquid_presence()``. This is a standalone method that can be called when you want the robot to react to a missing liquid or empty well. When ``require_liquid_presence()`` finds an empty well, it raises an error and pauses the protocol to let you resolve the problem. See also :ref:`lpd`.

.. code-block:: python
pipette.require_liquid_presence(reservoir["A1"])
pipette.aspirate(100, reservoir["A1"]) # only occurs if liquid found
.. versionadded:: 2.20
4 changes: 2 additions & 2 deletions api/docs/v2/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
# use rst_prolog to hold the subsitution
# update the apiLevel value whenever a new minor version is released
rst_prolog = f"""
.. |apiLevel| replace:: 2.19
.. |apiLevel| replace:: 2.20
.. |release| replace:: {release}
"""

Expand Down Expand Up @@ -445,7 +445,7 @@
("py:class", r".*protocol_api\.config.*"),
("py:class", r".*opentrons_shared_data.*"),
("py:class", r".*protocol_api._parameters.Parameters.*"),
("py:class", r".*AbsorbanceReaderContext"), # shh it's a secret (for now)
("py:class", r".*AbsorbanceReaderContext"),
("py:class", r".*RobotContext"), # shh it's a secret (for now)
("py:class", r'.*AbstractLabware|APIVersion|LabwareLike|LoadedCoreMap|ModuleTypes|NoneType|OffDeckType|ProtocolCore|WellCore'), # laundry list of not fully qualified things
]
6 changes: 1 addition & 5 deletions api/docs/v2/new_pipette.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ Pages in this section of the documentation cover:

- :ref:`Loading pipettes <loading-pipettes>` into your protocol.
- :ref:`Pipette characteristics <pipette-characteristics>`, such as how fast they can move liquid and how they move around the deck.
- The :ref:`partial tip pickup <partial-tip-pickup>` configuration for the Flex 96-Channel Pipette, which uses only 8 channels for pipetting. Full and partial tip pickup can be combined in a single protocol.
- :ref:`Partial tip pickup <partial-tip-pickup>` configurations for multi-channel pipettes. Full and partial tip pickup configurations can be combined in a single protocol.
- The :ref:`volume modes <pipette-volume-modes>` of Flex 50 µL pipettes, which must operate in low-volume mode to accurately dispense very small volumes of liquid.

For information about liquid handling, see :ref:`v2-atomic-commands` and :ref:`v2-complex-commands`.




Loading

0 comments on commit 4242c42

Please sign in to comment.