Skip to content

Commit 2b8eff0

Browse files
committed
2 parents 7fb4f11 + 8eb14ae commit 2b8eff0

File tree

216 files changed

+4819
-2081
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

216 files changed

+4819
-2081
lines changed

.github/workflows/app-test-build-deploy.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ jobs:
207207
needs: [determine-build-type]
208208
if: needs.determine-build-type.outputs.variants != '[]'
209209
strategy:
210+
fail-fast: false
210211
matrix:
211212
os: ['windows-2022', 'ubuntu-22.04', 'macos-latest']
212213
variant: ${{fromJSON(needs.determine-build-type.outputs.variants)}}

abr-testing/abr_testing/data_collection/abr_google_drive.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,13 @@ def create_data_dictionary(
7676
start_time = datetime.strptime(
7777
file_results.get("startedAt", ""), "%Y-%m-%dT%H:%M:%S.%f%z"
7878
)
79-
adjusted_start_time = start_time - timedelta(hours=5)
79+
adjusted_start_time = start_time - timedelta(hours=4)
8080
start_date = str(adjusted_start_time.date())
8181
start_time_str = str(adjusted_start_time).split("+")[0]
8282
complete_time = datetime.strptime(
8383
file_results.get("completedAt", ""), "%Y-%m-%dT%H:%M:%S.%f%z"
8484
)
85-
adjusted_complete_time = complete_time - timedelta(hours=5)
85+
adjusted_complete_time = complete_time - timedelta(hours=4)
8686
complete_time_str = str(adjusted_complete_time).split("+")[0]
8787
run_time = complete_time - start_time
8888
run_time_min = run_time.total_seconds() / 60

abr-testing/abr_testing/data_collection/abr_robot_error.py

Lines changed: 121 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,105 @@
11
"""Create ticket for robot with error."""
2-
from typing import List, Tuple, Any, Dict
2+
from typing import List, Tuple, Any, Dict, Optional
33
from abr_testing.data_collection import read_robot_logs, abr_google_drive, get_run_logs
44
import requests
55
import argparse
66
from abr_testing.automation import jira_tool, google_sheets_tool, google_drive_tool
77
import shutil
88
import os
99
import subprocess
10+
from datetime import datetime, timedelta
1011
import sys
1112
import json
1213
import re
1314
import pandas as pd
15+
from statistics import mean, StatisticsError
16+
17+
18+
def compare_current_trh_to_average(
19+
robot: str,
20+
start_time: Any,
21+
end_time: Optional[Any],
22+
protocol_name: str,
23+
storage_directory: str,
24+
) -> str:
25+
"""Get average temp/rh for errored run and compare to average."""
26+
# Connect to ABR ambient conditions sheet
27+
credentials_path = os.path.join(storage_directory, "credentials.json")
28+
temprh_data_sheet = google_sheets_tool.google_sheet(
29+
credentials_path, "ABR Ambient Conditions", 0
30+
)
31+
headers = temprh_data_sheet.get_row(1)
32+
all_trh_data = temprh_data_sheet.get_all_data(expected_headers=headers)
33+
# Connect to ABR-run-data sheet
34+
abr_data = google_sheets_tool.google_sheet(credentials_path, "ABR-run-data", 0)
35+
headers = abr_data.get_row(1)
36+
all_run_data = abr_data.get_all_data(expected_headers=headers)
37+
# Find average conditions of errored time period
38+
df_all_trh = pd.DataFrame(all_trh_data)
39+
# Convert timestamps to datetime objects
40+
df_all_trh["Timestamp"] = pd.to_datetime(
41+
df_all_trh["Timestamp"], format="mixed", utc=True
42+
).dt.tz_localize(None)
43+
# Ensure start_time is timezone-naive
44+
start_time = start_time.replace(tzinfo=None)
45+
relevant_temp_rhs = df_all_trh[
46+
(df_all_trh["Robot"] == robot) & (df_all_trh["Timestamp"] >= start_time)
47+
]
48+
try:
49+
avg_temp = round(mean(relevant_temp_rhs["Temp (oC)"]), 2)
50+
avg_rh = round(mean(relevant_temp_rhs["Relative Humidity (%)"]), 2)
51+
except StatisticsError:
52+
# If there is one value assign it as the average.
53+
if len(relevant_temp_rhs["Temp (oC)"]) == 1:
54+
avg_temp = relevant_temp_rhs["Temp (oC)"][0]
55+
avg_rh = relevant_temp_rhs["Relative Humidity (%)"][0]
56+
else:
57+
avg_temp = None
58+
avg_rh = None
59+
# Get AVG t/rh of runs w/ same robot & protocol newer than 3 wks old with no errors
60+
weeks_ago_3 = start_time - timedelta(weeks=3)
61+
df_all_run_data = pd.DataFrame(all_run_data)
62+
df_all_run_data["Start_Time"] = pd.to_datetime(
63+
df_all_run_data["Start_Time"], format="mixed", utc=True
64+
).dt.tz_localize(None)
65+
df_all_run_data["Errors"] = pd.to_numeric(df_all_run_data["Errors"])
66+
df_all_run_data["Average Temp (oC)"] = pd.to_numeric(
67+
df_all_run_data["Average Temp (oC)"]
68+
)
69+
common_filters = (
70+
(df_all_run_data["Robot"] == robot)
71+
& (df_all_run_data["Start_Time"] >= weeks_ago_3)
72+
& (df_all_run_data["Start_Time"] <= start_time)
73+
& (df_all_run_data["Errors"] < 1)
74+
& (df_all_run_data["Average Temp (oC)"] > 1)
75+
)
76+
77+
if protocol_name == "":
78+
relevant_run_data = df_all_run_data[common_filters]
79+
else:
80+
relevant_run_data = df_all_run_data[
81+
common_filters & (df_all_run_data["Protocol_Name"] == protocol_name)
82+
]
83+
# Calculate means of historical data
84+
try:
85+
historical_avg_temp = round(
86+
mean(relevant_run_data["Average Temp (oC)"].astype(float)), 2
87+
)
88+
historical_avg_rh = round(
89+
mean(relevant_run_data["Average RH(%)"].astype(float)), 2
90+
)
91+
except StatisticsError:
92+
historical_avg_temp = None
93+
historical_avg_rh = None
94+
# Formats TEMP/RH message for ticket.
95+
temp_rh_message = (
96+
f"{len(relevant_run_data)} runs with temp/rh data for {robot} running {protocol_name}."
97+
f" AVG TEMP (deg C): {historical_avg_temp}. AVG RH (%): {historical_avg_rh}."
98+
f" AVG TEMP of ERROR: {avg_temp}. AVG RH of ERROR: {avg_rh}."
99+
)
100+
# Print out comparison string.
101+
print(temp_rh_message)
102+
return temp_rh_message
14103

15104

16105
def compare_lpc_to_historical_data(
@@ -42,9 +131,9 @@ def compare_lpc_to_historical_data(
42131
current_x = round(labware_dict["X"], 2)
43132
current_y = round(labware_dict["Y"], 2)
44133
current_z = round(labware_dict["Z"], 2)
45-
avg_x = round(sum(x_float) / len(x_float), 2)
46-
avg_y = round(sum(y_float) / len(y_float), 2)
47-
avg_z = round(sum(z_float) / len(z_float), 2)
134+
avg_x = round(mean(x_float), 2)
135+
avg_y = round(mean(y_float), 2)
136+
avg_z = round(mean(z_float), 2)
48137

49138
# Formats LPC message for ticket.
50139
lpc_message = (
@@ -195,6 +284,15 @@ def get_robot_state(
195284
components = ["Flex-RABR"]
196285
components = match_error_to_component("RABR", reported_string, components)
197286
print(components)
287+
end_time = datetime.now()
288+
print(end_time)
289+
start_time = end_time - timedelta(hours=2)
290+
print(start_time)
291+
# Get current temp/rh compared to historical data
292+
temp_rh_string = compare_current_trh_to_average(
293+
parent, start_time, end_time, "", storage_directory
294+
)
295+
description["Robot Temp and RH Comparison"] = temp_rh_string
198296
whole_description_str = (
199297
"{"
200298
+ "\n".join("{!r}: {!r},".format(k, v) for k, v in description.items())
@@ -242,6 +340,23 @@ def get_run_error_info_from_robot(
242340
description["protocol_name"] = results["protocol"]["metadata"].get(
243341
"protocolName", ""
244342
)
343+
# Get start and end time of run
344+
start_time = datetime.strptime(
345+
results.get("startedAt", ""), "%Y-%m-%dT%H:%M:%S.%f%z"
346+
)
347+
adjusted_start_time = start_time - timedelta(hours=4)
348+
complete_time = datetime.strptime(
349+
results.get("completedAt", ""), "%Y-%m-%dT%H:%M:%S.%f%z"
350+
)
351+
adjusted_complete_time = complete_time - timedelta(hours=4)
352+
# Get average temp and rh of robot and protocol the error occurred on.
353+
temp_rh_comparison = compare_current_trh_to_average(
354+
parent,
355+
adjusted_start_time,
356+
adjusted_complete_time,
357+
description["protocol_name"],
358+
storage_directory,
359+
)
245360
# Get LPC coordinates of labware of failure
246361
lpc_dict = results["labwareOffsets"]
247362
labware_dict = results["labware"]
@@ -280,6 +395,7 @@ def get_run_error_info_from_robot(
280395
if len(lpc_message) < 1:
281396
lpc_message = "No LPC coordinates found in relation to error."
282397
description["LPC Comparison"] = lpc_message
398+
description["Robot Temp and RH Comparison"] = temp_rh_comparison
283399
all_modules = abr_google_drive.get_modules(results)
284400
whole_description = {**description, **all_modules}
285401
whole_description_str = (
@@ -352,6 +468,7 @@ def get_run_error_info_from_robot(
352468
email = args.email[0]
353469
board_id = args.board_id[0]
354470
reporter_id = args.reporter_id[0]
471+
file_paths = read_robot_logs.get_logs(storage_directory, ip)
355472
ticket = jira_tool.JiraTicket(url, api_token, email)
356473
ticket.issues_on_board(board_id)
357474
users_file_path = ticket.get_jira_users(storage_directory)
@@ -384,7 +501,6 @@ def get_run_error_info_from_robot(
384501
saved_file_path_calibration, calibration = read_robot_logs.get_calibration_offsets(
385502
ip, storage_directory
386503
)
387-
file_paths = read_robot_logs.get_logs(storage_directory, ip)
388504

389505
print(f"Making ticket for {summary}.")
390506
# TODO: make argument or see if I can get rid of with using board_id.

abr-testing/abr_testing/data_collection/read_robot_logs.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -569,23 +569,32 @@ def get_calibration_offsets(
569569

570570
def get_logs(storage_directory: str, ip: str) -> List[str]:
571571
"""Get Robot logs."""
572-
log_types = ["api.log", "server.log", "serial.log", "touchscreen.log"]
572+
log_types: List[Dict[str, Any]] = [
573+
{"log type": "api.log", "records": 1000},
574+
{"log type": "server.log", "records": 10000},
575+
{"log type": "serial.log", "records": 10000},
576+
{"log type": "touchscreen.log", "records": 1000},
577+
]
573578
all_paths = []
574579
for log_type in log_types:
575580
try:
581+
log_type_name = log_type["log type"]
582+
print(log_type_name)
583+
log_records = int(log_type["records"])
584+
print(log_records)
576585
response = requests.get(
577-
f"http://{ip}:31950/logs/{log_type}",
578-
headers={"log_identifier": log_type},
579-
params={"records": 5000},
586+
f"http://{ip}:31950/logs/{log_type_name}",
587+
headers={"log_identifier": log_type_name},
588+
params={"records": log_records},
580589
)
581590
response.raise_for_status()
582591
log_data = response.text
583-
log_name = ip + "_" + log_type.split(".")[0] + ".log"
592+
log_name = ip + "_" + log_type_name.split(".")[0] + ".log"
584593
file_path = os.path.join(storage_directory, log_name)
585594
with open(file_path, mode="w", encoding="utf-8") as file:
586595
file.write(log_data)
587596
except RuntimeError:
588-
print(f"Request exception. Did not save {log_type}")
597+
print(f"Request exception. Did not save {log_type_name}")
589598
continue
590599
all_paths.append(file_path)
591600
# Get weston.log using scp

analyses-snapshot-testing/tests/__snapshots__/analyses_snapshot_test/test_analysis_snapshot[d8cb88b3b2][Flex_S_v2_16_P1000_96_TC_PartialTipPickupSingle].json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3535,7 +3535,7 @@
35353535
"errors": [
35363536
{
35373537
"createdAt": "TIMESTAMP",
3538-
"detail": "ValueError [line 16]: Nozzle layout configuration of style SINGLE is currently unsupported.",
3538+
"detail": "ValueError [line 16]: Nozzle layout configuration of style SINGLE is unsupported in API Versions lower than 2.20.",
35393539
"errorCode": "4000",
35403540
"errorInfo": {},
35413541
"errorType": "ExceptionInProtocolError",
@@ -3544,10 +3544,10 @@
35443544
"wrappedErrors": [
35453545
{
35463546
"createdAt": "TIMESTAMP",
3547-
"detail": "ValueError: Nozzle layout configuration of style SINGLE is currently unsupported.",
3547+
"detail": "ValueError: Nozzle layout configuration of style SINGLE is unsupported in API Versions lower than 2.20.",
35483548
"errorCode": "4000",
35493549
"errorInfo": {
3550-
"args": "('Nozzle layout configuration of style SINGLE is currently unsupported.',)",
3550+
"args": "('Nozzle layout configuration of style SINGLE is unsupported in API Versions lower than 2.20.',)",
35513551
"class": "ValueError",
35523552
"traceback": " File \"/usr/local/lib/python3.10/site-packages/opentrons/protocols/execution/execute_python.py\", line N, in exec_run\n exec(\"run(__context)\", new_globs)\n\n File \"<string>\", line N, in <module>\n\n File \"Flex_S_v2_16_P1000_96_TC_PartialTipPickupSingle.py\", line N, in run\n\n File \"/usr/local/lib/python3.10/site-packages/opentrons/protocols/api_support/util.py\", line N, in _check_version_wrapper\n return decorated_obj(*args, **kwargs)\n\n File \"/usr/local/lib/python3.10/site-packages/opentrons/protocol_api/instrument_context.py\", line N, in configure_nozzle_layout\n raise ValueError(\n"
35533553
},

api-client/src/dataFiles/uploadCsvFile.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,18 @@ export function uploadCsvFile(
88
config: HostConfig,
99
data: FileData
1010
): ResponsePromise<UploadedCsvFileResponse> {
11-
return request<UploadedCsvFileResponse>(
11+
let formData
12+
13+
if (typeof data !== 'string') {
14+
formData = new FormData()
15+
formData.append('file', data)
16+
} else {
17+
formData = data
18+
}
19+
return request<UploadedCsvFileResponse, FormData | string>(
1220
POST,
1321
'/dataFiles',
14-
null,
15-
config,
16-
data
22+
formData,
23+
config
1724
)
1825
}

api-client/src/protocols/createProtocol.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@ import { POST, request } from '../request'
22
import type { ResponsePromise } from '../request'
33
import type { HostConfig } from '../types'
44
import type { Protocol } from './types'
5-
import type { RunTimeParameterCreateData } from '../runs'
5+
import type {
6+
RunTimeParameterValuesCreateData,
7+
RunTimeParameterFilesCreateData,
8+
} from '../runs'
69

710
export function createProtocol(
811
config: HostConfig,
912
files: File[],
1013
protocolKey?: string,
1114
protocolKind?: string,
12-
runTimeParameterValues?: RunTimeParameterCreateData
15+
runTimeParameterValues?: RunTimeParameterValuesCreateData,
16+
runTimeParameterFiles?: RunTimeParameterFilesCreateData
1317
): ResponsePromise<Protocol> {
1418
const formData = new FormData()
1519
files.forEach(file => {
@@ -22,6 +26,11 @@ export function createProtocol(
2226
'runTimeParameterValues',
2327
JSON.stringify(runTimeParameterValues)
2428
)
29+
if (runTimeParameterFiles != null)
30+
formData.append(
31+
'runTimeParameterFiles',
32+
JSON.stringify(runTimeParameterFiles)
33+
)
2534

2635
return request<Protocol, FormData>(POST, '/protocols', formData, config)
2736
}

api-client/src/protocols/createProtocolAnalysis.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,27 @@ import { POST, request } from '../request'
33
import type { ProtocolAnalysisSummary } from '@opentrons/shared-data'
44
import type { ResponsePromise } from '../request'
55
import type { HostConfig } from '../types'
6-
import type { RunTimeParameterCreateData } from '../runs'
6+
import type {
7+
RunTimeParameterFilesCreateData,
8+
RunTimeParameterValuesCreateData,
9+
} from '../runs'
710

811
interface CreateProtocolAnalysisData {
9-
runTimeParameterValues: RunTimeParameterCreateData
12+
runTimeParameterValues: RunTimeParameterValuesCreateData
13+
runTimeParameterFiles: RunTimeParameterFilesCreateData
1014
forceReAnalyze: boolean
1115
}
1216

1317
export function createProtocolAnalysis(
1418
config: HostConfig,
1519
protocolKey: string,
16-
runTimeParameterValues?: RunTimeParameterCreateData,
20+
runTimeParameterValues?: RunTimeParameterValuesCreateData,
21+
runTimeParameterFiles?: RunTimeParameterFilesCreateData,
1722
forceReAnalyze?: boolean
1823
): ResponsePromise<ProtocolAnalysisSummary[]> {
1924
const data = {
2025
runTimeParameterValues: runTimeParameterValues ?? {},
26+
runTimeParameterFiles: runTimeParameterFiles ?? {},
2127
forceReAnalyze: forceReAnalyze ?? false,
2228
}
2329
const response = request<

api-client/src/protocols/getCsvFiles.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { v4 as uuidv4 } from 'uuid'
2-
31
// import { GET, request } from '../request'
42

53
// import type { ResponsePromise } from '../request'
@@ -25,18 +23,16 @@ export function getCsvFiles(
2523
config: HostConfig,
2624
protocolId: string
2725
): Promise<{ data: UploadedCsvFilesResponse }> {
28-
const fileIdOne = uuidv4()
29-
const fileIdTwo = uuidv4()
3026
const stub = {
3127
data: {
3228
files: [
3329
{
34-
id: fileIdOne,
30+
id: '1',
3531
createdAt: '2024-06-07T19:19:56.268029+00:00',
3632
name: 'rtp_mock_file1.csv',
3733
},
3834
{
39-
id: fileIdTwo,
35+
id: '2',
4036
createdAt: '2024-06-17T19:19:56.268029+00:00',
4137
name: 'rtp_mock_file2.csv',
4238
},

0 commit comments

Comments
 (0)