Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion .github/workflows/chem-sync-local-flask-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
name: Quality Checks + Tests
defaults:
run:
working-directory: ./examples/chem-sync-local-flask
working-directory: ./examples/benchling-apps/chem-sync-local-flask

steps:
- uses: actions/checkout@v3
Expand Down
50 changes: 46 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Benchling App Python Examples
# Benchling Python Examples

The `examples/` directory contains Benchling App samples written by Benchling.
Copy link
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.

# Benchling Apps

## chem-sync-local-flask

Expand All @@ -11,12 +13,52 @@ Uses [Cloudflare-tunnel](https://www.cloudflare.com/products/tunnel/) and [Docke
in a local development environment running [Flask](https://flask.palletsprojects.com/) with the
[Benchling SDK](https://docs.benchling.com/docs/getting-started-with-the-sdk).

![image info](./examples/chem-sync-local-flask/docs/demo-short.gif)
![image info](./examples/benchling-apps/chem-sync-local-flask/docs/demo-short.gif)

**Code Includes:**
* Benchling App Authentication via [Client Credentials](https://docs.benchling.com/docs/getting-started-benchling-apps#getting-credentials)
* Custom UI via [App Canvas](https://docs.benchling.com/docs/introduction-to-app-canvas)
* 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 within Benchling Analysis.

#### [Example Snippets](./examples/custom-code-AD/README.md)

![image info](./examples/custom-code-AD/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](./examples/custom-code-AD/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-AD/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Custom Code in Automation Designer

## plot-chromatogram

Example snippet demonstrates plotting a multi-axis HPLC chromatogram (retention volume X Absorbance (mAU), Temperature (degC), pH) with custom code.

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

- **[Input File](./snippets/plot-chromatogram/docs/input/HPLC_Chromtogram_Plot%20(Absorbance,%20pH,%20Temperature).csv)**
- **[Custom Code Block](./snippets/plot-chromatogram/HPLC_Chromatogram_Plot_(Absorbance,%20pH,%20Temperature).py)**
- **[Output File](./snippets/plot-chromatogram/docs/output/Chromatogram_New.png)**
12 changes: 12 additions & 0 deletions examples/custom-code-AD/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
allotropy==0.1.105
biopython==1.86
lmfit==1.3.4
numpy==2.2.4
openpyxl==3.1.5
pandas==2.2.3
plotly==5.22.0
pyarrow==19.0.1
pydantic==2.12.5
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,102 @@

"""
Supported packages:

allotropy
biopython
lmfit
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