Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Benchling App Python Examples
# Benchling Python Examples

The `examples/` directory contains Benchling App samples written by Benchling.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will be good to structure the README a little more:

I'm thinking we should have H1 for each category, and H2 headers (and short description) for each example in the category (and we can even use collapsible summary)

Benchling Apps:

...

chem-sync-local-flask

Pre-existing description...

Custom Code in Automation Designer

Contains code snippets, include link to README for snippets

plot-chromatogram

short description...

A collection of Python reference examples for the Benchling platform.

## chem-sync-local-flask

Expand All @@ -19,4 +19,23 @@ in a local development environment running [Flask](https://flask.palletsprojects
* User Feedback via [App Status](https://docs.benchling.com/docs/introduction-to-app-status)
* Data Mapping via [App Config](https://docs.benchling.com/docs/app-configuration)
* Receiving and verifying [Webhooks](https://docs.benchling.com/docs/getting-started-with-webhooks)
* Creating [molecule custom entities](https://benchling.com/api/reference#/Molecules/createMolecule)
* Creating [molecule custom entities](https://benchling.com/api/reference#/Molecules/createMolecule)

## Custom Code in Automation Designer
This project contains example snippets of code that can be utilized in a Custom Code step of the Benchling Automation Designer with Benchling Analysis.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea I think an overview statement should be sufficient here, and then we can just link to the README in examples/sippets/README.md so the rest of the content here will just be in there (rather than duplicated)


![image info](./examples/custom-code-in-automation-designer/snippets/plot-chromatogram/docs/Example_Chromatogram_Plot.gif)

## Overview
These examples demonstrate how to extend Benchling's native capabilities using Python scripts within the Automation Designer context. They cover common use cases such as data visualization, file handling, and complex data transformations.

## Key Capabilities
The code examples included in this directory cover the following functionalities:

- Visualizations: Create custom charts, graphs, and annotations (e.g., chromatograms).

- File Parsing: Logic to read and parse various file formats.

- File Creation: Generate new files, such as instruction lists for laboratory instruments.

- Data Transformation: Apply transformations, merge/join datasets, and perform complex calculations on data.
36 changes: 36 additions & 0 deletions examples/custom-code-in-automation-designer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Custom Code in Automation Designer
This repository contains Python snippets compatible with the Custom Code feature in Automation Designer. This feature allows users to execute Python logic directly within Benchling Analysis.

![image info](./snippets/plot-chromatogram/docs/Example_Chromatogram_Plot.gif)

## Overview
These examples demonstrate how to extend Benchling's native capabilities using Python scripts within the Automation Designer context. They cover common use cases such as data visualization, file handling, and complex data transformations.

## Key Capabilities
The code examples included in this directory cover the following functionalities:

- Visualizations: Create custom charts, graphs, and annotations (e.g., chromatograms).

- File Parsing: Logic to read and parse various file formats.

- File Creation: Generate new files, such as instruction lists for laboratory instruments.

- Data Transformation: Apply transformations, merge/join datasets, and perform complex calculations on data.

## Dependencies
See [requirements.txt](./requirements.txt) for the specific library versions used in these examples.

## Constraints & Limitations
When adapting these examples for your tenant, please note the current beta limitations:

- Runtime Limit: Execution is limited to 15 minutes per run.

- No Network Access: The environment does not support general network access.

- Fixed Packages: You cannot install custom libraries (e.g., via `pip`). You are limited to the pre-installed packages listed above

- No API Access: The Benchling SDK/API is not currently supported within the execution environment.

- Bring your own Container: BYOC is not supported; code runs in the standard Benchling runtime environment

- Lifecycle Management: The feature does not currently support native GitHub integration or versioning within the UI.
11 changes: 11 additions & 0 deletions examples/custom-code-in-automation-designer/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
allotropy==0.1.111
lmfit==1.2.0
numpy==2.2.4
openpyxl==3.1.5
pandas==2.2.3
plotly==5.22.0
pyarrow==19.0.1
pydantic==1.10.21
scikit-learn==1.6.1
scipy==1.15.2
statsmodels==0.14.4
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@

"""
Supported packages:

allotropy
benchling-sdk
Comment thread
james-leinas marked this conversation as resolved.
Outdated
numpy
openpyxl
pandas
plotly
pyarrow
pydantic
scikit-learn
scipy
statsmodels
"""
from io import BytesIO
import pandas as pd
from typing import NamedTuple
import plotly.graph_objects as go
from scipy.signal import find_peaks

class IOData(NamedTuple):
name: str
data: BytesIO | pd.DataFrame | go.Figure

def custom_code(inputs: list[IOData], **kwargs) -> list[IOData]:
df = inputs[0].data
# Extract the relevant data series and convert them to floats
absorbance_data = df['absorbance (mAU)'].astype(float)
retention_volume_data = df['retention volume (mL)'].astype(float)

# Find peaks in the absorbance data.
# We set a `height` threshold to ignore baseline noise.
# A good starting point is 10% of the max absorbance.
min_height = absorbance_data.max() * 0.10
peak_indices, _ = find_peaks(absorbance_data, height=min_height)

# Set a default range in case no peaks are found
x_axis_range = None

# Check if at least one peak was detected
if len(peak_indices) > 0:
# Get the retention volumes for the first and last detected peaks
first_peak_x = retention_volume_data.iloc[peak_indices[0]]
last_peak_x = retention_volume_data.iloc[peak_indices[-1]]

# Define the padding you want on each side
padding = 150

# Calculate the new x-axis range
x_min = first_peak_x - padding
x_max = last_peak_x + padding

x_axis_range = [x_min, x_max]
fig = go.Figure()

# Add Absorbance Trace
fig.add_trace(go.Scatter(
x=df['retention volume (mL)'],
y=df['absorbance (mAU)'],
name="Absorbance (mAU)",
line=dict(color='royalblue'),
yaxis="y1"
))

# Add other traces (pH, Conc. B)
fig.add_trace(go.Scatter(
x=df['retention volume (mL)'], y=df['pH (pH)'],
name="pH", line=dict(color='crimson', dash='dash'), yaxis="y2"
))
fig.add_trace(go.Scatter(
x=df['retention volume (mL)'], y=df['temperature (degC)'],
name="Temperature (degC)", line=dict(color='green', dash='dot'), yaxis="y3"
))

# --- Layout Definition ---
fig.update_layout(
title_text="Chromatogram with Abs, pH, Temperature Traces",
xaxis_title="Retention Volume (mL)",
plot_bgcolor='white',
legend_title="Traces",
xaxis=dict(domain=[0.1, 0.88]),
yaxis=dict(
title="<b>Absorbance (mAU)</b>",
tickfont=dict(color="royalblue"),
color="royalblue"
),
yaxis2=dict(
title="<b>pH</b>",
tickfont=dict(color="crimson"), color="crimson",
anchor="x", overlaying="y", side="right"
),
yaxis3=dict(
title="<b>Temperature (degC)</b>",
tickfont=dict(color="green"), color="green",
anchor="free", overlaying="y", side="right", position=0.92
)
)

return [IOData(name="Chromatogram_New", data=fig)]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading