Skip to content

Commit 2172685

Browse files
authored
Connect Robot Ambient Conditions and Run Data (#15504)
<!-- Thanks for taking the time to open a pull request! Please make sure you've read the "Opening Pull Requests" section of our Contributing Guide: https://github.com/Opentrons/opentrons/blob/edge/CONTRIBUTING.md#opening-pull-requests To ensure your code is reviewed quickly and thoroughly, please fill out the sections below to the best of your ability! --> # Overview Adds RH and Temp to ABR Data Sheet # Test Plan Ran script with both sheets. Rows updated successfully. # Changelog <!-- List out the changes to the code in this PR. Please try your best to categorize your changes and describe what has changed and why. Example changelog: - Fixed app crash when trying to calibrate an illegal pipette - Added state to API to track pipette usage - Updated API docs to mention only two pipettes are supported IMPORTANT: MAKE SURE ANY BREAKING CHANGES ARE PROPERLY COMMUNICATED --> # Review requests <!-- Describe any requests for your reviewers here. --> # Risk assessment <!-- Carefully go over your pull request and look at the other parts of the codebase it may affect. Look for the possibility, even if you think it's small, that your change may affect some other part of the system - for instance, changing return tip behavior in protocol may also change the behavior of labware calibration. Identify the other parts of the system your codebase may affect, so that in addition to your own review and testing, other people who may not have the system internalized as much as you can focus their attention and testing there. -->
1 parent ee62429 commit 2172685

File tree

9 files changed

+478
-162
lines changed

9 files changed

+478
-162
lines changed

abr-testing/Pipfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ opentrons-hardware = {editable = true, path = "./../hardware", extras=['FLEX']}
1616
opentrons = {editable = true, path = "./../api", extras=['flex-hardware']}
1717
slackclient = "*"
1818
slack-sdk = "*"
19+
scikit-learn = "*"
20+
pandas = "*"
1921

2022
[dev-packages]
2123
atomicwrites = "==1.4.1"

abr-testing/Pipfile.lock

Lines changed: 295 additions & 154 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

abr-testing/abr_testing/automation/google_drive_tool.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,27 @@ def share_permissions(self, file_id: str) -> None:
197197
fileId=file_id, body=new_permission, transferOwnership=False # type: ignore
198198
).execute()
199199

200+
def download_single_file(
201+
self, save_directory: str, file_id: str, file_name: str, mime_type: str
202+
) -> str:
203+
"""Download single file."""
204+
# google sheets: text/csv
205+
file_path = ""
206+
if mime_type:
207+
request = self.drive_service.files().export_media(
208+
fileId=file_id, mimeType=mime_type
209+
)
210+
else:
211+
request = self.drive_service.files().get_media(fileId=file_id)
212+
file_path = os.path.join(save_directory, file_name)
213+
fh = io.FileIO(file_path, "wb")
214+
downloader = MediaIoBaseDownload(fh, request)
215+
done = False
216+
while done is False:
217+
status, done = downloader.next_chunk()
218+
print(f"Downloading {file_name}... {int(status.progress() * 100)}%")
219+
return file_path
220+
200221
def download_files(
201222
self, files_to_download: List[Dict[str, Any]], save_directory: str
202223
) -> None:

abr-testing/abr_testing/automation/google_sheets_tool.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def column_letter_to_index(column_letter: str) -> int:
142142
try:
143143
float_value = float(value)
144144
user_entered_value = {"numberValue": float_value}
145-
except ValueError:
145+
except (ValueError, TypeError):
146146
user_entered_value = {"stringValue": str(value)}
147147
requests.append(
148148
{
@@ -172,14 +172,20 @@ def update_cell(
172172
self.spread_sheet.worksheet(sheet_title).update_cell(row, column, single_data)
173173
return row, column, single_data
174174

175-
def get_all_data(self) -> List[Dict[str, Any]]:
175+
def get_all_data(
176+
self, expected_headers: Optional[Set[str]]
177+
) -> List[Dict[str, Any]]:
176178
"""Get all the Data recorded from worksheet."""
177-
return self.worksheet.get_all_records()
179+
return self.worksheet.get_all_records(expected_headers=expected_headers)
178180

179181
def get_column(self, column_number: int) -> Set[str]:
180182
"""Get all values in column."""
181183
return self.worksheet.col_values(column_number)
182184

185+
def get_row(self, row_number: int) -> Set[str]:
186+
"""Get all values in row."""
187+
return self.worksheet.row_values(row_number)
188+
183189
def get_cell(self, sheet_title: str, cell: str) -> Any:
184190
"""Get cell value with location ex: 'A1'."""
185191
return self.spread_sheet.worksheet(sheet_title).acell(cell).value

abr-testing/abr_testing/data_collection/abr_google_drive.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ def create_data_dictionary(
115115
plate_measure = {
116116
"Plate Measured": plate,
117117
"End Volume Accuracy (%)": accuracy,
118+
"Average Temp (oC)": "",
119+
"Average RH(%)": "",
118120
}
119121
row_for_lpc = {**row, **all_modules, **notes}
120122
row_2 = {
@@ -203,6 +205,7 @@ def create_data_dictionary(
203205
) = create_data_dictionary(missing_runs_from_gs, storage_directory, "", "", "")
204206

205207
start_row = google_sheet.get_index_row() + 1
208+
print(start_row)
206209
google_sheet.batch_update_cells(transposed_runs_and_robots, "A", start_row, "0")
207210

208211
# Add LPC to google sheet

abr-testing/abr_testing/data_collection/abr_robot_error.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,8 @@ def get_run_error_info_from_robot(
255255
print(f"Making ticket for {summary}.")
256256
# TODO: make argument or see if I can get rid of with using board_id.
257257
project_key = "RABR"
258-
parent_key = project_key + "-" + robot[-1]
258+
print(robot)
259+
parent_key = project_key + "-" + robot.split("ABR")[1]
259260
# TODO: read board to see if ticket for run id already exists.
260261
# CREATE TICKET
261262
issue_key = ticket.create_ticket(

abr-testing/abr_testing/tools/abr_lpc.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ def remove_duplicate_data() -> None:
1010
seen = set()
1111
new_values = []
1212
row_indices = []
13-
sheet_data = google_sheet_lpc.get_all_data()
13+
headers = google_sheet_lpc.get_row(1)
14+
sheet_data = google_sheet_lpc.get_all_data(headers)
1415
for i, row in enumerate(sheet_data):
1516
key = (
1617
row["Robot"],
@@ -49,8 +50,10 @@ def remove_duplicate_data() -> None:
4950
except FileNotFoundError:
5051
print(f"Add credentials.json file to: {storage_directory}.")
5152
sys.exit()
53+
5254
google_sheet_lpc = google_sheets_tool.google_sheet(credentials_path, "ABR-LPC", 0)
53-
print(len(google_sheet_lpc.get_all_data()))
55+
headers = google_sheet_lpc.get_row(1)
56+
print(len(google_sheet_lpc.get_all_data(headers)))
5457
remove_duplicate_data()
55-
num_of_rows = print(len(google_sheet_lpc.get_all_data()))
58+
num_of_rows = print(len(google_sheet_lpc.get_all_data(headers)))
5659
# TODO: automate data analysis

abr-testing/abr_testing/tools/abr_scale.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ def get_all_plate_readings(
4242
) -> float:
4343
"""Calculate accuracy of liquid moved on final measurement step."""
4444
accuracy = 0.0
45-
all_data = google_sheet.get_all_data()
45+
header_list = google_sheet.get_row(1)
46+
all_data = google_sheet.get_all_data(header_list)
47+
4648
# Get mass of first reading
4749
mass_1_readings = []
4850
for row in all_data:
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
"""Add temperature and humidity data to ABR-run-data sheet."""
2+
3+
from abr_testing.automation import google_sheets_tool
4+
from abr_testing.automation import google_drive_tool
5+
import argparse
6+
import csv
7+
import sys
8+
import os
9+
from typing import Dict, Tuple, Any, List
10+
from statistics import mean, StatisticsError
11+
12+
13+
def add_robot_lifetime(abr_data: List[Dict[str, Any]]) -> None:
14+
"""Add % Robot Lifetime to each run."""
15+
# TODO: add robot lifetime to each run.
16+
17+
18+
def compare_run_to_temp_data(
19+
abr_data: List[Dict[str, Any]], temp_data: List[Dict[str, Any]], google_sheet: Any
20+
) -> None:
21+
"""Read ABR Data and compare robot and timestamp columns to temp data."""
22+
row_update = 0
23+
for run in abr_data:
24+
run_id = run["Run_ID"]
25+
try:
26+
average_temp = float(run["Average Temp (oC)"])
27+
except ValueError:
28+
average_temp = 0
29+
if len(run_id) < 1 or average_temp > 0:
30+
continue
31+
else:
32+
# Determine which runs do not have average temp/rh data
33+
temps = []
34+
rel_hums = []
35+
for recording in temp_data:
36+
temp_robot = recording["Robot"]
37+
if len(recording["Timestamp"]) > 1:
38+
timestamp = recording["Timestamp"]
39+
if (
40+
temp_robot == run["Robot"]
41+
and timestamp >= run["Start_Time"]
42+
and timestamp <= run["End_Time"]
43+
):
44+
temps.append(float(recording["Temp (oC)"]))
45+
rel_hums.append(float(recording["Relative Humidity (%)"]))
46+
try:
47+
avg_temps = mean(temps)
48+
avg_humidity = mean(rel_hums)
49+
row_num = google_sheet.get_row_index_with_value(run_id, 2)
50+
# Write average temperature
51+
google_sheet.update_cell("Sheet1", row_num, 46, avg_temps)
52+
# Write average humidity
53+
google_sheet.update_cell("Sheet1", row_num, 47, avg_humidity)
54+
# TODO: Write averages to google sheet
55+
print(f"Updated row {row_num}.")
56+
except StatisticsError:
57+
avg_temps = None
58+
avg_humidity = None
59+
print(f"Updated {row_update} rows with temp and RH data.")
60+
61+
62+
def read_csv_as_dict(file_path: str) -> List[Dict[str, Any]]:
63+
"""Read a CSV file and return its content as a list of dictionaries."""
64+
with open(file_path, mode="r", newline="", encoding="utf-8") as csvfile:
65+
reader = csv.DictReader(csvfile)
66+
data = [row for row in reader]
67+
return data
68+
69+
70+
def connect_and_download(
71+
sheets: Dict[str, str], storage_directory: str
72+
) -> Tuple[List[str], str]:
73+
"""Connect to google sheet and download."""
74+
try:
75+
credentials_path = os.path.join(storage_directory, "credentials.json")
76+
google_drive = google_drive_tool.google_drive(
77+
credentials_path,
78+
"1W8S3EV3cIfC-ZoRF3km0ad5XqyVkO3Tu",
79+
80+
)
81+
print("connected to gd")
82+
except FileNotFoundError:
83+
print(f"Add credentials.json file to: {storage_directory}.")
84+
sys.exit()
85+
file_paths = []
86+
for sheet in sheets.items():
87+
file_name, file_id = sheet[0], sheet[1]
88+
print(file_name)
89+
file_path = google_drive.download_single_file(
90+
storage_directory, file_id, file_name, "text/csv"
91+
)
92+
file_paths.append(file_path)
93+
return file_paths, credentials_path
94+
95+
96+
if __name__ == "__main__":
97+
parser = argparse.ArgumentParser(
98+
description="Adds average robot ambient conditions to run sheet."
99+
)
100+
parser.add_argument(
101+
"--abr-data-sheet",
102+
type=str,
103+
default="1M6LSLNwvWuHQOwIwUpblF_Eyx4W5y5gXgdU3rjU2XFk",
104+
help="end of url of main data sheet.",
105+
)
106+
parser.add_argument(
107+
"--room-conditions-sheet",
108+
type=str,
109+
default="1cIjSvK_mPCq4IFqUPB7SgdDuuMKve5kJh0xyH4znAd0",
110+
help="end fo url of ambient conditions data sheet",
111+
)
112+
parser.add_argument(
113+
"--storage-directory",
114+
type=str,
115+
default="C:/Users/Rhyann Clarke/test_folder",
116+
help="Path to long term storage directory for run logs.",
117+
)
118+
args = parser.parse_args()
119+
google_sheets_to_download = {
120+
"ABR-run-data": args.abr_data_sheet,
121+
"ABR Ambient Conditions": args.room_conditions_sheet,
122+
}
123+
storage_directory = args.storage_directory
124+
# Download google sheets.
125+
file_paths, credentials_path = connect_and_download(
126+
google_sheets_to_download, storage_directory
127+
)
128+
# TODO: read csvs.
129+
abr_data = read_csv_as_dict(file_paths[0])
130+
temp_data = read_csv_as_dict(file_paths[1])
131+
# TODO: compare robot and timestamps.
132+
abr_google_sheet = google_sheets_tool.google_sheet(
133+
credentials_path, "ABR-run-data", 0
134+
)
135+
136+
compare_run_to_temp_data(abr_data, temp_data, abr_google_sheet)
137+
# TODO: Write average for matching cells.

0 commit comments

Comments
 (0)