├── LICENSE ├── README.md ├── blob_upload ├── Pipfile ├── Pipfile.lock ├── README.md ├── blob_upload.py ├── file_helpers.py └── requirements.txt ├── bulk_create_into_benchling ├── Pipfile ├── Pipfile.lock ├── README.md ├── antibodies.json ├── bulk_create_into_benchling.py ├── images │ ├── antibody-fields.png │ ├── chain-schema.png │ └── entities-in-folder.png └── requirements.txt ├── events_examples └── benchling_request_to_slack │ ├── README.md │ └── lambda_function.py ├── sync_into_benchling ├── Pipfile ├── Pipfile.lock ├── README.md ├── antibodies.json ├── images │ ├── antibody-fields.png │ ├── chain-schema.png │ └── entities-in-folder.png ├── requirements.txt └── sync_into_benchling.py ├── sync_out_of_benchling ├── Pipfile ├── Pipfile.lock ├── README.md ├── requirements.txt └── sync_out_of_benchling.py └── upload_results ├── Pipfile ├── Pipfile.lock ├── README.md ├── images ├── api-key.png ├── example-plate-reader-run.png ├── insert-from-inbox.png ├── insert-run.png ├── results.png └── user-menu.png ├── plate_reader_data.csv ├── requirements.txt └── upload.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Benchling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > Note: This repository contains legacy examples that have not been kept up-to-date. These examples are being preserved for posterity, but should be treated as legacy code. **Do not use this code in production**; tread with caution if using these examples for anything other than learning. 2 | 3 | This repository contains examples of how to use Benchling's API. 4 | 5 | ## Examples 6 | 7 | - `sync_into_benchling/` shows how to import and register entities 8 | - `sync_out_of_benchling/` shows how to export all registered entities modified after a certain timestamp 9 | - `upload_results/` shows how to upload data from a plate reader as structured results 10 | - `blob_upload/` shows how to upload a blob attachment 11 | -------------------------------------------------------------------------------- /blob_upload/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | certifi = "==2021.5.30" 8 | chardet = "==4.0.0" 9 | idna = "==2.10" 10 | requests = "==2.25.1" 11 | urllib3 = "==1.26.6" 12 | click = "*" 13 | 14 | [dev-packages] 15 | 16 | [requires] 17 | python_version = "3.9" 18 | -------------------------------------------------------------------------------- /blob_upload/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "df076d3f15d06c8d95009d81f6b145982df64e623161cf2277570249a9e422ea" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.9" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee", 22 | "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8" 23 | ], 24 | "index": "pypi", 25 | "version": "==2021.5.30" 26 | }, 27 | "chardet": { 28 | "hashes": [ 29 | "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", 30 | "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" 31 | ], 32 | "index": "pypi", 33 | "version": "==4.0.0" 34 | }, 35 | "click": { 36 | "hashes": [ 37 | "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a", 38 | "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6" 39 | ], 40 | "index": "pypi", 41 | "version": "==8.0.1" 42 | }, 43 | "idna": { 44 | "hashes": [ 45 | "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", 46 | "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" 47 | ], 48 | "index": "pypi", 49 | "version": "==2.10" 50 | }, 51 | "requests": { 52 | "hashes": [ 53 | "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", 54 | "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" 55 | ], 56 | "index": "pypi", 57 | "version": "==2.25.1" 58 | }, 59 | "urllib3": { 60 | "hashes": [ 61 | "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4", 62 | "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f" 63 | ], 64 | "index": "pypi", 65 | "version": "==1.26.6" 66 | } 67 | }, 68 | "develop": {} 69 | } 70 | -------------------------------------------------------------------------------- /blob_upload/README.md: -------------------------------------------------------------------------------- 1 | # **Upload Blobs To Benchling** 2 | 3 | Example code for uploading blobs to Benchling, part of the [Uploading Instrument Results](https://docs.benchling.com/docs/example-creating-results) guide available on Benchling Developer Platform Documentation Page. 4 | 5 | # How to run the script 6 | 7 | - First, ask Benchling support to enable API access on your account, and create API credentials. Instructions: https://help.benchling.com/articles/2353570-access-the-benchling-api-enterprise 8 | - Install Python 3 and [Pipenv](https://docs.pipenv.org/en/latest/) 9 | - Install dependencies using `pipenv install` 10 | - Run `pipenv shell` to work in a virtualenv that includes the dependencies. 11 | - Run the script. For example: 12 | 13 | ``` 14 | python blob_upload.py \ 15 | --domain example.benchling.com \ 16 | --api-key $YOUR_API_KEY \ 17 | --filepath path/to/chromatogram_file 18 | ``` -------------------------------------------------------------------------------- /blob_upload/blob_upload.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | import click 5 | import requests 6 | import time 7 | 8 | from file_helpers import calculate_md5, encode_base64 9 | 10 | CHUNK_SIZE_BYTES = int(10e6) 11 | 12 | class BadRequestException(Exception): 13 | def __init__(self, message, rv): 14 | super(BadRequestException, self).__init__(message) 15 | self.rv = rv 16 | 17 | 18 | def api_post(domain, api_key, path, body): 19 | url = "https://{}/api/v2/{}".format(domain, path) 20 | rv = requests.post(url, json=body, auth=(api_key, "")) 21 | if rv.status_code >= 400: 22 | raise BadRequestException( 23 | "Server returned status {}. Response:\n{}".format( 24 | rv.status_code, json.dumps(rv.json()) 25 | ), 26 | rv, 27 | ) 28 | return rv.json() 29 | 30 | 31 | @click.command() 32 | @click.option( 33 | "--domain", 34 | help="Domain name of your Benchling instance, e.g. example.benchling.com", 35 | required=True, 36 | ) 37 | @click.option("--api-key", help="Your API key", required=True) 38 | @click.option("--filepath", help="Filepath of blob to upload", required=True) 39 | @click.option("--destination-filename", help="Name of file (omit to keep same name as source)", required=False) 40 | def main( 41 | domain, 42 | api_key, 43 | filepath, 44 | destination_filename, 45 | ): 46 | name = destination_filename 47 | if name is None: 48 | name = os.path.basename(filepath) 49 | file_size = os.path.getsize(filepath) 50 | with open(filepath, "rb") as file: 51 | if file_size <= CHUNK_SIZE_BYTES: 52 | upload_single_part_blob(api_key, domain, file, name) 53 | else: 54 | upload_multi_part_blob(api_key, domain, file, name) 55 | 56 | 57 | def upload_single_part_blob(api_key, domain, file, name): 58 | file_contents = file.read() 59 | encoded64 = encode_base64(file_contents) 60 | md5 = calculate_md5(file_contents) 61 | res = api_post(domain, api_key, "blobs", { 62 | "data64": encoded64, 63 | "md5": md5, 64 | "mimeType": "application/octet-stream", 65 | "name": name, 66 | "type": "RAW_FILE", 67 | }) 68 | assert(res["uploadStatus"] == "COMPLETE") 69 | print("Finished uploading {} with blob ID {}".format( 70 | res["name"], res["id"] 71 | )) 72 | 73 | 74 | def upload_multi_part_blob(api_key, domain, file, name): 75 | chunk_producer = lambda chunk_size: file.read(chunk_size) 76 | start_blob = api_post(domain, api_key, "blobs:start-multipart-upload", { 77 | "mimeType": "application/octet-stream", 78 | "name": name, 79 | "type": "RAW_FILE", 80 | }) 81 | part_number = 0 82 | blob_parts = [] 83 | try: 84 | while True: 85 | cursor = chunk_producer(CHUNK_SIZE_BYTES) 86 | if not cursor: 87 | break 88 | part_number += 1 89 | encoded64 = encode_base64(cursor) 90 | md5 = calculate_md5(cursor) 91 | created_part = api_post(domain, api_key, "blobs/{}/parts".format(start_blob["id"]), { 92 | "data64": encoded64, 93 | "md5": md5, 94 | "partNumber": part_number, 95 | }) 96 | blob_parts.append(created_part) 97 | api_post(domain, api_key, "blobs/{}:complete-upload".format(start_blob["id"]), { 98 | "parts": blob_parts 99 | }) 100 | print("Completed uploading {} parts for blob {}".format(part_number, start_blob["id"])) 101 | except Exception as e: 102 | print("Error while uploading part {} for blob {}".format(part_number, start_blob["id"])) 103 | api_post(domain, api_key, "blobs/{}:abort-upload".format(start_blob["id"]), {}) 104 | raise e 105 | 106 | if __name__ == "__main__": 107 | main() 108 | -------------------------------------------------------------------------------- /blob_upload/file_helpers.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import hashlib 3 | 4 | 5 | def encode_base64(input: bytes, charset: str = "utf-8") -> str: 6 | file_bytes = base64.encodebytes(input) 7 | return str(file_bytes, charset) 8 | 9 | 10 | def calculate_md5(input: bytes) -> str: 11 | return hashlib.md5(input).hexdigest() 12 | -------------------------------------------------------------------------------- /blob_upload/requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2021.5.30 2 | chardet==4.0.0 3 | idna==2.10 4 | requests==2.25.1 5 | urllib3==1.26.6 6 | -------------------------------------------------------------------------------- /bulk_create_into_benchling/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | click = "*" 10 | requests = "*" 11 | 12 | [requires] 13 | python_version = "3.7" 14 | -------------------------------------------------------------------------------- /bulk_create_into_benchling/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "85d29b2cfc5d4e61d41de6948621b329e2e1c6d692edeabc70fd0d8413e298db" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.7" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", 22 | "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" 23 | ], 24 | "version": "==2019.6.16" 25 | }, 26 | "chardet": { 27 | "hashes": [ 28 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 29 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 30 | ], 31 | "version": "==3.0.4" 32 | }, 33 | "click": { 34 | "hashes": [ 35 | "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", 36 | "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" 37 | ], 38 | "index": "pypi", 39 | "version": "==7.0" 40 | }, 41 | "idna": { 42 | "hashes": [ 43 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 44 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 45 | ], 46 | "version": "==2.8" 47 | }, 48 | "requests": { 49 | "hashes": [ 50 | "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", 51 | "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" 52 | ], 53 | "index": "pypi", 54 | "version": "==2.22.0" 55 | }, 56 | "urllib3": { 57 | "hashes": [ 58 | "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", 59 | "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" 60 | ], 61 | "version": "==1.25.3" 62 | } 63 | }, 64 | "develop": {} 65 | } 66 | -------------------------------------------------------------------------------- /bulk_create_into_benchling/README.md: -------------------------------------------------------------------------------- 1 | This script demonstrates how to use the bulk create into registry entity endpoints. The script uses long running tasks and demonstrates how to handle them. In this example, we are importing antibodies defined in a JSON file: 2 | 3 | - Each Antibody entity links to a Heavy Chain and a Light Chain entity 4 | - The Heavy Chain and Light Chain are amino acid (AA) sequences 5 | 6 | The antibodies to import are provided in the following format: 7 | 8 | ``` 9 | { 10 | "name": "AB-BRCA2-0001", 11 | "Heavy Chain": "AAAAAAAAAAAAAAAAAAAAAAAA", 12 | "Light Chain": "CCCCCCCCCCCCCCCCCCCCCCCC" 13 | } 14 | ``` 15 | 16 | # Prerequisites 17 | 18 | This script requires the Registry and Molecular Biology applications. In addition, the script expects two **entity schemas** to be configured ([instructions](https://help.benchling.com/articles/2725066-configure-your-registry)). 19 | 20 | - An Antibody schema with Heavy Chain and Light Chain fields: ![Antibody fields](images/antibody-fields.png) 21 | - A Chain schema with the AA Sequence entity type and a unique constraint on the amino acid sequence: ![Chain schema](images/chain-schema.png) 22 | 23 | Finally, you need the ID of a folder to import into. You can find or [create](https://help.benchling.com/articles/2724910-create-folders-to-organize-data-within-projects) a folder, and then use the API to [list all folders](https://docs.benchling.com/v2/reference#list-folders) and find the ID of your folder. 24 | 25 | # How to run the script 26 | 27 | - First, ask Benchling support to enable API access on your account, and create API credentials. Instructions: https://help.benchling.com/articles/2353570-access-the-benchling-api-enterprise 28 | - Install Python 3 and [Pipenv](https://docs.pipenv.org/en/latest/) 29 | - Install dependencies using `pipenv install` 30 | - Run `pipenv shell` to work in a virtualenv that includes the dependencies. 31 | - Run the script. For example (replace the folder/registry/schema IDs with your own): 32 | 33 | ``` 34 | python bulk_create_into_benchling.py \ 35 | --domain example.benchling.com \ 36 | --api-key $YOUR_API_KEY \ 37 | --folder-id lib_9NmU9eFB \ 38 | --registry-id src_Lmysq16b \ 39 | --antibody-schema-id ts_LpAfe6xV \ 40 | --chain-schema-id ts_M9ft0HsP \ 41 | antibodies.json 42 | ``` 43 | 44 | If successful, the script will print the Registry IDs of the new Antibody and Chain entities: 45 | 46 | ``` 47 | Registered new Antibody TA001 with Heavy Chain C-577,397 and Light Chain C-448,864 48 | Registered new Antibody TA002 with Heavy Chain C-495,242 and Light Chain C-875,143 49 | Registered new Antibody TA003 with Heavy Chain C-495,242 and Light Chain C-227,055 50 | ``` 51 | 52 | ## How to view the imported entities 53 | 54 | To view the imported entities, open the folder that they were imported into: ![Entities in folder](images/entities-in-folder.png) 55 | -------------------------------------------------------------------------------- /bulk_create_into_benchling/antibodies.json: -------------------------------------------------------------------------------- 1 | { 2 | "antibodies": [ 3 | { 4 | "name": "AB-BRCA2-0001", 5 | "Heavy Chain": "AAAAAAAAAAAAAAAAAAAAAAAA", 6 | "Light Chain": "CCCCCCCCCCCCCCCCCCCCCCCC" 7 | }, 8 | { 9 | "name": "AB-BRCA2-0002", 10 | "Heavy Chain": "DDDDDDDDDDDDDDDDDDDDDDDD", 11 | "Light Chain": "EEEEEEEEEEEEEEEEEEEEEEEE" 12 | }, 13 | { 14 | "name": "AB-BRCA2-0003", 15 | "Heavy Chain": "DDDDDDDDDDDDDDDDDDDDDDDD", 16 | "Light Chain": "FFFFFFFFFFFFFFFFFFFFFFFF" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /bulk_create_into_benchling/bulk_create_into_benchling.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import click 4 | import requests 5 | import time 6 | 7 | 8 | class BadRequestException(Exception): 9 | def __init__(self, message, rv): 10 | super(BadRequestException, self).__init__(message) 11 | self.rv = rv 12 | 13 | 14 | def api_get(domain, api_key, path): 15 | url = "https://{}/api/v2/{}".format(domain, path) 16 | rv = requests.get(url, auth=(api_key, "")) 17 | if rv.status_code >= 400: 18 | raise BadRequestException( 19 | "Server returned status {}. Response:\n{}".format( 20 | rv.status_code, json.dumps(rv.json()) 21 | ), 22 | rv, 23 | ) 24 | return rv.json() 25 | 26 | 27 | def api_post(domain, api_key, path, body): 28 | url = "https://{}/api/v2/{}".format(domain, path) 29 | rv = requests.post(url, json=body, auth=(api_key, "")) 30 | if rv.status_code >= 400: 31 | raise BadRequestException( 32 | "Server returned status {}. Response:\n{}".format( 33 | rv.status_code, json.dumps(rv.json()) 34 | ), 35 | rv, 36 | ) 37 | return rv.json() 38 | 39 | 40 | def wait_on_task_response(domain, api_key, task_resource): 41 | task_id = task_resource["taskId"] 42 | while True: 43 | task_response = api_get(domain, api_key, "tasks/{}".format(task_id)) 44 | status = task_response["status"] 45 | if status == "RUNNING": 46 | time.sleep(10) 47 | else: 48 | if status == "FAILED": 49 | return "FAILED", task_response["message"], task_response["errors"] 50 | elif status == "SUCCEEDED": 51 | return "SUCCEEDED", task_response["response"] 52 | 53 | 54 | @click.command() 55 | @click.option( 56 | "--domain", 57 | help="Domain name of your Benchling instance, e.g. example.benchling.com", 58 | required=True, 59 | ) 60 | @click.option("--api-key", help="Your API key", required=True) 61 | @click.option( 62 | "--folder-id", help="ID of a folder to create the antibody in", required=True 63 | ) 64 | @click.option("--registry-id", help="ID of the Benchling Registry", required=True) 65 | @click.option("--antibody-schema-id", help="ID of the Antibody schema", required=True) 66 | @click.option( 67 | "--chain-schema-id", 68 | help="ID of the Chain schema (Must be an AA-Sequence)", 69 | required=True, 70 | ) 71 | @click.argument("json_file_to_import", type=click.File("r")) 72 | def main( 73 | domain, 74 | api_key, 75 | antibody_schema_id, 76 | registry_id, 77 | chain_schema_id, 78 | folder_id, 79 | json_file_to_import, 80 | ): 81 | antibodies_obj = json.loads(json_file_to_import.read()) 82 | antibody_objs = antibodies_obj["antibodies"] 83 | 84 | # Bulk create heavy chains into registry. 85 | task_resource = api_post( 86 | domain, 87 | api_key, 88 | # https://docs.benchling.com/reference#bulk-create-aa-sequences 89 | "aa-sequences:bulk-create", 90 | { 91 | "aaSequences": [ 92 | { 93 | "aminoAcids": antibody_obj["Heavy Chain"], 94 | "folderId": folder_id, 95 | "name": "Heavy Chain for {}".format(antibody_obj["name"]), 96 | "schemaId": chain_schema_id, 97 | "registryId": registry_id, 98 | "namingStrategy": "NEW_IDS", 99 | } 100 | for antibody_obj in antibody_objs 101 | ] 102 | }, 103 | ) 104 | task_response = wait_on_task_response(domain, api_key, task_resource) 105 | if task_response[0] == "FAILED": 106 | print( 107 | "Could not register at least one heavy chain. Error response from server:\n{}\n{}".format( 108 | task_response[1], 109 | task_response[2], 110 | ) 111 | ) 112 | return 113 | 114 | bulk_registered_heavy_chain_response_obj = task_response[1]["aaSequences"] 115 | print("Successfully registered heavy chains") 116 | 117 | # Bulk create light chains into registry. 118 | task_resource = api_post( 119 | domain, 120 | api_key, 121 | # https://docs.benchling.com/reference#bulk-create-aa-sequences 122 | "aa-sequences:bulk-create", 123 | { 124 | "aaSequences": [ 125 | { 126 | "aminoAcids": antibody_obj["Light Chain"], 127 | "folderId": folder_id, 128 | "name": "Light Chain for {}".format(antibody_obj["name"]), 129 | "schemaId": chain_schema_id, 130 | "registryId": registry_id, 131 | "namingStrategy": "NEW_IDS", 132 | } 133 | for antibody_obj in antibody_objs 134 | ] 135 | }, 136 | ) 137 | 138 | task_response = wait_on_task_response(domain, api_key, task_resource) 139 | if task_response[0] == "FAILED": 140 | print( 141 | "Could not register at least one light chain. Error response from server:\n{}\n{}".format( 142 | task_response[1], 143 | task_response[2], 144 | ) 145 | ) 146 | return 147 | 148 | bulk_registered_light_chain_response_obj = task_response[1]["aaSequences"] 149 | print("Successfully registered light chains") 150 | 151 | # Bulk create antibodies in registry. 152 | task_resource = api_post( 153 | domain, 154 | api_key, 155 | # https://docs.benchling.com/reference#bulk-create-custom-entities 156 | "custom-entities:bulk-create", 157 | { 158 | "customEntities": [ 159 | { 160 | "name": antibody_obj["name"], 161 | "schemaId": antibody_schema_id, 162 | "folderId": folder_id, 163 | "registryId": registry_id, 164 | "namingStrategy": "NEW_IDS", 165 | "fields": { 166 | "Heavy Chain": {"value": heavy_chain_obj["entityRegistryId"]}, 167 | "Light Chain": {"value": light_chain_obj["entityRegistryId"]}, 168 | }, 169 | } 170 | for antibody_obj, heavy_chain_obj, light_chain_obj in zip( 171 | antibody_objs, 172 | bulk_registered_heavy_chain_response_obj, 173 | bulk_registered_light_chain_response_obj, 174 | ) 175 | ] 176 | }, 177 | ) 178 | task_response = wait_on_task_response(domain, api_key, task_resource) 179 | if task_response[0] == "FAILED": 180 | print( 181 | "Could not register at least one antibody. Error response from server:\n{}\n{}".format( 182 | task_response[1], 183 | task_response[2], 184 | ) 185 | ) 186 | return 187 | bulk_registred_antibody_response_obj = task_response[1]["customEntities"] 188 | 189 | for antibody_obj, heavy_chain_obj, light_chain_obj in zip( 190 | bulk_registred_antibody_response_obj, 191 | bulk_registered_heavy_chain_response_obj, 192 | bulk_registered_light_chain_response_obj, 193 | ): 194 | 195 | print( 196 | "Registered new Antibody {} with Heavy Chain {} and Light Chain {}".format( 197 | antibody_obj["entityRegistryId"], 198 | heavy_chain_obj["entityRegistryId"], 199 | light_chain_obj["entityRegistryId"], 200 | ) 201 | ) 202 | 203 | 204 | if __name__ == "__main__": 205 | main() 206 | -------------------------------------------------------------------------------- /bulk_create_into_benchling/images/antibody-fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benchling/integration-examples/79b192a5ebe20373734b1f888f26f612b8f2f29f/bulk_create_into_benchling/images/antibody-fields.png -------------------------------------------------------------------------------- /bulk_create_into_benchling/images/chain-schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benchling/integration-examples/79b192a5ebe20373734b1f888f26f612b8f2f29f/bulk_create_into_benchling/images/chain-schema.png -------------------------------------------------------------------------------- /bulk_create_into_benchling/images/entities-in-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benchling/integration-examples/79b192a5ebe20373734b1f888f26f612b8f2f29f/bulk_create_into_benchling/images/entities-in-folder.png -------------------------------------------------------------------------------- /bulk_create_into_benchling/requirements.txt: -------------------------------------------------------------------------------- 1 | -i https://pypi.org/simple 2 | certifi==2019.6.16 3 | chardet==3.0.4 4 | click==7.0 5 | idna==2.8 6 | requests==2.22.0 7 | urllib3==1.25.3 8 | -------------------------------------------------------------------------------- /events_examples/benchling_request_to_slack/README.md: -------------------------------------------------------------------------------- 1 | # **Push Benchling Request Notification to Slack** 2 | 3 | AWS Lambda function in lambda_function.py is part of the [Benchling Events Example Guide](https://docs.benchling.com/docs/example-push-benchling-request-notification-to-slack) available on Benchling Developer Platform Documentation Page. 4 | -------------------------------------------------------------------------------- /events_examples/benchling_request_to_slack/lambda_function.py: -------------------------------------------------------------------------------- 1 | # lambda_function.py 2 | 3 | import json 4 | import os 5 | import urllib3 6 | 7 | 8 | def lambda_handler(event, context): 9 | http = urllib3.PoolManager() 10 | assignee_handles = [] 11 | for assignee in event["detail"]["request"]["assignees"]: 12 | assignee_handles.append("@" + str(assignee["user"]["handle"])) 13 | 14 | # Create data payload for Slack POST request 15 | # see https://api.slack.com/block-kit for more formatting options for the message 16 | data = { 17 | "blocks": [ 18 | { 19 | "type": "section", 20 | "text": { 21 | "type": "mrkdwn", 22 | "text": " New Benchling Request Here!\n" 23 | + "<" 24 | + event["detail"]["request"]["webURL"] 25 | + "|Go To Benchling Request >", 26 | }, 27 | }, 28 | { 29 | "type": "section", 30 | "fields": [ 31 | { 32 | "type": "mrkdwn", 33 | "text": " *Requestor*:\n" 34 | + "@" 35 | + str(event["detail"]["request"]["creator"]["handle"]), 36 | }, 37 | {"type": "mrkdwn", "text": " *Assignee(s)*:\n" + ", ".join(assignee_handles)}, 38 | { 39 | "type": "mrkdwn", 40 | "text": " *Comments*:\n" 41 | + str(event["detail"]["request"]["fields"]["request_comments"]["value"]), 42 | }, 43 | ], 44 | }, 45 | {"type": "divider"}, 46 | ] 47 | } 48 | 49 | # Send POST request to webhook URL generated in Slack App admin settings 50 | resp = http.request( 51 | "POST", 52 | os.environ["SLACK_WEBHOOK_URL"], 53 | body=json.dumps(data), 54 | headers={ 55 | "Content-Type": "application/json", 56 | "Authorization": "Bearer {}".format(os.environ["SLACK_TOKEN"]), 57 | }, 58 | ) 59 | 60 | return {"statusCode": resp.status, "statusData": resp.data} 61 | -------------------------------------------------------------------------------- /sync_into_benchling/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | click = "*" 10 | requests = "*" 11 | 12 | [requires] 13 | python_version = "3.7" 14 | -------------------------------------------------------------------------------- /sync_into_benchling/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "85d29b2cfc5d4e61d41de6948621b329e2e1c6d692edeabc70fd0d8413e298db" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.7" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", 22 | "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" 23 | ], 24 | "version": "==2019.6.16" 25 | }, 26 | "chardet": { 27 | "hashes": [ 28 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 29 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 30 | ], 31 | "version": "==3.0.4" 32 | }, 33 | "click": { 34 | "hashes": [ 35 | "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", 36 | "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" 37 | ], 38 | "index": "pypi", 39 | "version": "==7.0" 40 | }, 41 | "idna": { 42 | "hashes": [ 43 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 44 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 45 | ], 46 | "version": "==2.8" 47 | }, 48 | "requests": { 49 | "hashes": [ 50 | "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", 51 | "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" 52 | ], 53 | "index": "pypi", 54 | "version": "==2.22.0" 55 | }, 56 | "urllib3": { 57 | "hashes": [ 58 | "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", 59 | "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" 60 | ], 61 | "version": "==1.25.3" 62 | } 63 | }, 64 | "develop": {} 65 | } 66 | -------------------------------------------------------------------------------- /sync_into_benchling/README.md: -------------------------------------------------------------------------------- 1 | This script demonstrates how to import and register entities. In this example, we are importing antibodies defined in a JSON file: 2 | 3 | - Each Antibody entity links to a Heavy Chain and a Light Chain entity 4 | - The Heavy Chain and Light Chain are amino acid (AA) sequences 5 | 6 | The antibodies to import are provided in the following format: 7 | 8 | ``` 9 | { 10 | "name": "AB-BRCA2-001", 11 | "Heavy Chain": "AAAAAAAAAAAAAAAAAAAAAAAA", 12 | "Light Chain": "CCCCCCCCCCCCCCCCCCCCCCCC" 13 | } 14 | ``` 15 | 16 | # Prerequisites 17 | 18 | This script requires the Registry and Molecular Biology applications. In addition, the script expects two **entity schemas** to be configured ([instructions](https://help.benchling.com/articles/2725066-configure-your-registry)). 19 | 20 | - An Antibody schema with Heavy Chain and Light Chain fields: ![Antibody fields](images/antibody-fields.png) 21 | - A Chain schema with the AA Sequence entity type and a unique constraint on the amino acid sequence: ![Chain schema](images/chain-schema.png) 22 | 23 | Finally, you need the ID of a folder to import into. You can find or [create](https://help.benchling.com/articles/2724910-create-folders-to-organize-data-within-projects) a folder, and then use the API to [list all folders](https://docs.benchling.com/v2/reference#list-folders) and find the ID of your folder. 24 | 25 | # How to run the script 26 | 27 | - First, ask Benchling support to enable API access on your account, and create API credentials. Instructions: https://help.benchling.com/articles/2353570-access-the-benchling-api-enterprise 28 | - Install Python 3 and [Pipenv](https://docs.pipenv.org/en/latest/) 29 | - Install dependencies using `pipenv install` 30 | - Run `pipenv shell` to work in a virtualenv that includes the dependencies. 31 | - Run the script. For example (replace the folder/registry/schema IDs with your own): 32 | 33 | ``` 34 | python sync_into_benchling.py \ 35 | --domain example.benchling.com \ 36 | --api-key $YOUR_API_KEY \ 37 | --folder-id lib_9NmU9eFB \ 38 | --registry-id src_Lmysq16b \ 39 | --antibody-schema-id ts_LpAfe6xV \ 40 | --chain-schema-id ts_M9ft0HsP \ 41 | antibodies.json 42 | ``` 43 | 44 | If successful, the script will print the Registry IDs of the new Antibody and Chain entities: 45 | 46 | ``` 47 | Registered new Antibody TA001 with Heavy Chain C-577,397 and Light Chain C-448,864 48 | Registered new Antibody TA002 with Heavy Chain C-495,242 and Light Chain C-875,143 49 | Registered new Antibody TA003 with Heavy Chain C-495,242 and Light Chain C-227,055 50 | ``` 51 | 52 | ## How to view the imported entities 53 | 54 | To view the imported entities, open the folder that they were imported into: ![Entities in folder](images/entities-in-folder.png) 55 | -------------------------------------------------------------------------------- /sync_into_benchling/antibodies.json: -------------------------------------------------------------------------------- 1 | { 2 | "antibodies": [ 3 | { 4 | "name": "AB-BRCA2-001", 5 | "Heavy Chain": "AAAAAAAAAAAAAAAAAAAAAAAA", 6 | "Light Chain": "CCCCCCCCCCCCCCCCCCCCCCCC" 7 | }, 8 | { 9 | "name": "AB-BRCA2-002", 10 | "Heavy Chain": "DDDDDDDDDDDDDDDDDDDDDDDD", 11 | "Light Chain": "EEEEEEEEEEEEEEEEEEEEEEEE" 12 | }, 13 | { 14 | "name": "AB-BRCA2-003", 15 | "Heavy Chain": "DDDDDDDDDDDDDDDDDDDDDDDD", 16 | "Light Chain": "FFFFFFFFFFFFFFFFFFFFFFFF" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /sync_into_benchling/images/antibody-fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benchling/integration-examples/79b192a5ebe20373734b1f888f26f612b8f2f29f/sync_into_benchling/images/antibody-fields.png -------------------------------------------------------------------------------- /sync_into_benchling/images/chain-schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benchling/integration-examples/79b192a5ebe20373734b1f888f26f612b8f2f29f/sync_into_benchling/images/chain-schema.png -------------------------------------------------------------------------------- /sync_into_benchling/images/entities-in-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benchling/integration-examples/79b192a5ebe20373734b1f888f26f612b8f2f29f/sync_into_benchling/images/entities-in-folder.png -------------------------------------------------------------------------------- /sync_into_benchling/requirements.txt: -------------------------------------------------------------------------------- 1 | -i https://pypi.org/simple 2 | certifi==2019.6.16 3 | chardet==3.0.4 4 | click==7.0 5 | idna==2.8 6 | requests==2.22.0 7 | urllib3==1.25.3 8 | -------------------------------------------------------------------------------- /sync_into_benchling/sync_into_benchling.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import click 4 | import requests 5 | 6 | 7 | class BadRequestException(Exception): 8 | def __init__(self, message, rv): 9 | super(BadRequestException, self).__init__(message) 10 | self.rv = rv 11 | 12 | 13 | def api_get(domain, api_key, path): 14 | url = "https://{}/api/v2/{}".format(domain, path) 15 | rv = requests.get(url, auth=(api_key, "")) 16 | if rv.status_code >= 400: 17 | raise BadRequestException( 18 | "Server returned status {}. Response:\n{}".format( 19 | rv.status_code, json.dumps(rv.json()) 20 | ), 21 | rv, 22 | ) 23 | return rv.json() 24 | 25 | 26 | def api_post(domain, api_key, path, body): 27 | url = "https://{}/api/v2/{}".format(domain, path) 28 | rv = requests.post(url, json=body, auth=(api_key, "")) 29 | if rv.status_code >= 400: 30 | raise BadRequestException( 31 | "Server returned status {}. Response:\n{}".format( 32 | rv.status_code, json.dumps(rv.json()) 33 | ), 34 | rv, 35 | ) 36 | return rv.json() 37 | 38 | 39 | def get_existing_registered_chain_with_aa_sequence( 40 | domain, api_key, chain_schema_id, aa_sequence 41 | ): 42 | """ 43 | Get the existing registered Chain with the given AA sequence, if one exists. 44 | 45 | If no registered Chain has the same AA sequence, return None. 46 | """ 47 | response = api_get( 48 | domain, 49 | api_key, 50 | # https://docs.benchling.com/v2/reference#list-amino-acid-sequences 51 | "aa-sequences?schemaId={}&aminoAcids={}".format(chain_schema_id, aa_sequence), 52 | ) 53 | matching_registered_chains = [ 54 | chain_json 55 | for chain_json in response["aaSequences"] 56 | # Ignore unregistered Chain entities 57 | if chain_json["entityRegistryId"] is not None 58 | ] 59 | 60 | if len(matching_registered_chains) > 0: 61 | # The Chain schema has a unique constraint on the AA sequence, so there should never be 62 | # multiple registered Chain entities with the same AA sequence. 63 | assert ( 64 | len(matching_registered_chains) == 1 65 | ), "Expected only one Chain with AA sequence {}".format(aa_sequence) 66 | [chain_json] = matching_registered_chains 67 | return chain_json 68 | else: 69 | return None 70 | 71 | 72 | def find_or_create_chain_in_registry_with_aa_sequence( 73 | domain, api_key, folder_id, registry_id, chain_schema_id, chain_name, aa_sequence 74 | ): 75 | """ 76 | Find or create a Chain entity with the given amino acid sequence. 77 | 78 | :returns: an AA Sequence Resource (https://docs.benchling.com/reference#protein-resource) 79 | """ 80 | # The Chain schema has a unique constraint on the AA sequence, 81 | # so if there's already a registered Chain with the same sequence, 82 | # we should use the existing Chain instead of creating a new one. 83 | existing_registered_chain_json = get_existing_registered_chain_with_aa_sequence( 84 | domain, api_key, chain_schema_id, aa_sequence 85 | ) 86 | if existing_registered_chain_json is not None: 87 | return existing_registered_chain_json 88 | 89 | # No Chain was registered with the same AA sequence, so create a new one in the registry. 90 | chain_json = api_post( 91 | domain, 92 | api_key, 93 | # https://docs.benchling.com/v2/reference#create-protein 94 | "aa-sequences", 95 | { 96 | "aminoAcids": aa_sequence, 97 | "folderId": folder_id, 98 | "name": chain_name, 99 | "schemaId": chain_schema_id, 100 | "registryId": registry_id, 101 | "namingStrategy": "NEW_IDS", 102 | }, 103 | ) 104 | return chain_json 105 | 106 | 107 | @click.command() 108 | @click.option( 109 | "--domain", 110 | help="Domain name of your Benchling instance, e.g. example.benchling.com", 111 | required=True, 112 | ) 113 | @click.option("--api-key", help="Your API key", required=True) 114 | @click.option( 115 | "--folder-id", help="ID of a folder to create the antibody in", required=True 116 | ) 117 | @click.option("--registry-id", help="ID of the Benchling Registry", required=True) 118 | @click.option("--antibody-schema-id", help="ID of the Antibody schema", required=True) 119 | @click.option( 120 | "--chain-schema-id", 121 | help="ID of the Chain schema (Must be an AA-Sequence)", 122 | required=True, 123 | ) 124 | @click.argument("json_file_to_import", type=click.File("r")) 125 | def main( 126 | domain, 127 | api_key, 128 | antibody_schema_id, 129 | registry_id, 130 | chain_schema_id, 131 | folder_id, 132 | json_file_to_import, 133 | ): 134 | antibodies_json = json.loads(json_file_to_import.read()) 135 | 136 | for antibody_json in antibodies_json["antibodies"]: 137 | # Create Heavy Chain in registry 138 | heavy_chain_json = find_or_create_chain_in_registry_with_aa_sequence( 139 | domain, 140 | api_key, 141 | folder_id, 142 | registry_id, 143 | chain_schema_id, 144 | "Heavy Chain for {}".format(antibody_json["name"]), 145 | antibody_json["Heavy Chain"], 146 | ) 147 | 148 | # Create Light Chain in registry 149 | light_chain_json = find_or_create_chain_in_registry_with_aa_sequence( 150 | domain, 151 | api_key, 152 | folder_id, 153 | registry_id, 154 | chain_schema_id, 155 | "Light Chain for {}".format(antibody_json["name"]), 156 | antibody_json["Light Chain"], 157 | ) 158 | 159 | try: 160 | # Create antibody in registry 161 | registered_antibody_response_json = api_post( 162 | domain, 163 | api_key, 164 | # https://docs.benchling.com/reference#create-custom-entity 165 | "custom-entities", 166 | { 167 | "name": antibody_json["name"], 168 | "schemaId": antibody_schema_id, 169 | "folderId": folder_id, 170 | "registryId": registry_id, 171 | "namingStrategy": "NEW_IDS", 172 | "fields": { 173 | "Heavy Chain": {"value": heavy_chain_json["entityRegistryId"]}, 174 | "Light Chain": {"value": light_chain_json["entityRegistryId"]}, 175 | }, 176 | }, 177 | ) 178 | except BadRequestException as e: 179 | if e.rv.status_code == 400: 180 | print( 181 | "Could not register {}. Error response from server:\n{}".format( 182 | antibody_json["name"], json.dumps(e.rv.json()) 183 | ) 184 | ) 185 | continue 186 | else: 187 | raise e 188 | 189 | print( 190 | "Registered new Antibody {} with Heavy Chain {} and Light Chain {}".format( 191 | registered_antibody_response_json["entityRegistryId"], 192 | heavy_chain_json["entityRegistryId"], 193 | light_chain_json["entityRegistryId"], 194 | ) 195 | ) 196 | 197 | 198 | if __name__ == "__main__": 199 | main() 200 | -------------------------------------------------------------------------------- /sync_out_of_benchling/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | click = "*" 10 | requests = "*" 11 | 12 | [requires] 13 | python_version = "3.7" 14 | -------------------------------------------------------------------------------- /sync_out_of_benchling/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "85d29b2cfc5d4e61d41de6948621b329e2e1c6d692edeabc70fd0d8413e298db" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.7" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", 22 | "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" 23 | ], 24 | "version": "==2019.6.16" 25 | }, 26 | "chardet": { 27 | "hashes": [ 28 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 29 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 30 | ], 31 | "version": "==3.0.4" 32 | }, 33 | "click": { 34 | "hashes": [ 35 | "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", 36 | "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" 37 | ], 38 | "index": "pypi", 39 | "version": "==7.0" 40 | }, 41 | "idna": { 42 | "hashes": [ 43 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 44 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 45 | ], 46 | "version": "==2.8" 47 | }, 48 | "requests": { 49 | "hashes": [ 50 | "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", 51 | "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" 52 | ], 53 | "index": "pypi", 54 | "version": "==2.22.0" 55 | }, 56 | "urllib3": { 57 | "hashes": [ 58 | "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", 59 | "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" 60 | ], 61 | "version": "==1.25.3" 62 | } 63 | }, 64 | "develop": {} 65 | } 66 | -------------------------------------------------------------------------------- /sync_out_of_benchling/README.md: -------------------------------------------------------------------------------- 1 | This script demonstrates how to export all registered entities that were modified after a certain timestamp, so that the changes can be imported into some external system. 2 | 3 | In this example, we are exporting entities as a CSV printed to standard output. For example, if the script is asked to export all Antibody entities exported after June 26, 2019, it could print a CSV like this: 4 | 5 | ``` 6 | Registry ID,Name,Last Modified At,Heavy Chain,Light Chain 7 | TA003,AB-BRCA2-003,2019-06-27T20:58:21.225189+00:00,Heavy Chain for AB-BRCA2-002,Light Chain for AB-BRCA2-003 8 | TA002,AB-BRCA2-002,2019-06-27T20:58:21.225189+00:00,Heavy Chain for AB-BRCA2-002,Light Chain for AB-BRCA2-002 9 | TA001,AB-BRCA2-001,2019-06-27T20:58:21.225189+00:00,Heavy Chain for AB-BRCA2-001,Light Chain for AB-BRCA2-001 10 | ``` 11 | 12 | # Prerequisites 13 | 14 | This script requires the Registry application. In addition, an **entity schema** must be [configured](https://help.benchling.com/articles/2725066-configure-your-registry), and at least one entity must have been [created and registered](https://help.benchling.com/en/articles/2725346-create-and-register-a-single-entity). 15 | 16 | # How to run the script 17 | 18 | - First, ask Benchling support to enable API access on your account, and create API credentials. Instructions: https://help.benchling.com/articles/2353570-access-the-benchling-api-enterprise 19 | - Install Python 3 and [Pipenv](https://docs.pipenv.org/en/latest/) 20 | - Install dependencies using `pipenv install` 21 | - Run `pipenv shell` to work in a virtualenv that includes the dependencies. 22 | - Run the script. For example: 23 | 24 | ``` 25 | python sync_out_of_benchling.py \ 26 | --domain example.benchling.com \ 27 | --api-key $YOUR_API_KEY \ 28 | --registry-id src_Lmysq16b \ 29 | --antibody-schema-id ts_LpAfe6xV \ 30 | --last-sync-timestamp 2020-06-27T20:58:17.464834+00:00 31 | ``` 32 | 33 | - The script should print a CSV like this: 34 | 35 | ``` 36 | Registry ID,Name,Last Modified At,Heavy Chain,Light Chain 37 | TA003,AB-BRCA2-003,2019-06-27T20:58:21.225189+00:00,Heavy Chain for AB-BRCA2-002,Light Chain for AB-BRCA2-003 38 | TA002,AB-BRCA2-002,2019-06-27T20:58:21.225189+00:00,Heavy Chain for AB-BRCA2-002,Light Chain for AB-BRCA2-002 39 | TA001,AB-BRCA2-001,2019-06-27T20:58:21.225189+00:00,Heavy Chain for AB-BRCA2-001,Light Chain for AB-BRCA2-001 40 | ``` 41 | -------------------------------------------------------------------------------- /sync_out_of_benchling/requirements.txt: -------------------------------------------------------------------------------- 1 | -i https://pypi.org/simple 2 | certifi==2019.6.16 3 | chardet==3.0.4 4 | click==7.0 5 | idna==2.8 6 | requests==2.22.0 7 | urllib3==1.25.3 8 | -------------------------------------------------------------------------------- /sync_out_of_benchling/sync_out_of_benchling.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import json 3 | import sys 4 | 5 | import click 6 | import requests 7 | 8 | 9 | def api_get(domain, api_key, path, params={}): 10 | url = "https://{}/api/v2/{}".format(domain, path) 11 | rv = requests.get(url, auth=(api_key, ""), params=params) 12 | if rv.status_code >= 400: 13 | raise Exception( 14 | "Server returned status {}. Response:\n{}".format( 15 | rv.status_code, json.dumps(rv.json()) 16 | ) 17 | ) 18 | return rv.json() 19 | 20 | 21 | @click.command() 22 | @click.option( 23 | "--domain", 24 | help="Domain name of your Benchling instance, e.g. example.benchling.com", 25 | required=True, 26 | ) 27 | @click.option("--api-key", help="Your API key", required=True) 28 | @click.option( 29 | "--registry-id", 30 | help=( 31 | "ID of the Benchling Registry. " 32 | "To get the ID of your registry, you can get a list of all registries " 33 | "from the List Registries endpoint (https://docs.benchling.com/reference#get-registries) " 34 | 'and copy the "id" field of your registry.' 35 | ), 36 | required=True, 37 | ) 38 | @click.option("--antibody-schema-id", help="ID of the Antibody schema", required=True) 39 | @click.option( 40 | "--last-sync-timestamp", 41 | help=( 42 | "Timestamp of the previous sync, in RFC 3339 format (e.g. 2019-06-27T20:58:21.225189+00:00). " 43 | "If not given, this script will export all Antibody entities." 44 | ), 45 | ) 46 | def main(domain, api_key, registry_id, antibody_schema_id, last_sync_timestamp): 47 | """Export registered Antibody entities that were modified after the given timestamp. 48 | 49 | The entities are exported as a CSV and printed to standard output. 50 | 51 | Example output: 52 | 53 | Registry ID,Name,Last Modified At,Heavy Chain,Light Chain 54 | TA003,AB-BRCA2-003,2019-06-27T20:58:21.225189+00:00,Heavy Chain for AB-BRCA2-002,Light Chain for AB-BRCA2-003 55 | """ 56 | # Get ordered field names so we can keep the CSV columns in a consistent order 57 | all_schemas_json = api_get( 58 | domain, 59 | api_key, 60 | # https://docs.benchling.com/v2/reference#list-entity-schemas 61 | "registries/{registry_id}/entity-schemas".format(registry_id=registry_id), 62 | )["entitySchemas"] 63 | [antibody_schema_json] = [ 64 | schema_json 65 | for schema_json in all_schemas_json 66 | if schema_json["id"] == antibody_schema_id 67 | ] 68 | antibody_field_names = [ 69 | field_definition_json["name"] 70 | for field_definition_json in antibody_schema_json["fieldDefinitions"] 71 | ] 72 | 73 | # Get all modified Antibody entities. There may be multiple pages, 74 | # so we need to keep calling the API until all entities have been returned. 75 | newly_modified_antibodies_json = [] 76 | next_token = None 77 | while True: 78 | response_json = api_get( 79 | domain, 80 | api_key, 81 | # https://docs.benchling.com/v2/reference#list-custom-entities 82 | "custom-entities", 83 | params={ 84 | "registryId": registry_id, 85 | "schemaId": antibody_schema_id, 86 | "modifiedAt": ( 87 | "> {timestamp}".format(timestamp=last_sync_timestamp) 88 | if last_sync_timestamp 89 | else None 90 | ), 91 | "nextToken": next_token, 92 | }, 93 | ) 94 | newly_modified_antibodies_json += response_json["customEntities"] 95 | next_token = response_json["nextToken"] 96 | if not next_token: 97 | break 98 | 99 | # Write the CSV 100 | writer = csv.DictWriter( 101 | sys.stdout, 102 | fieldnames=["Registry ID", "Name", "Last Modified At"] + antibody_field_names, 103 | ) 104 | writer.writeheader() 105 | for antibody_json in newly_modified_antibodies_json: 106 | csv_row_json = { 107 | "Registry ID": antibody_json["entityRegistryId"], 108 | "Name": antibody_json["name"], 109 | "Last Modified At": antibody_json["modifiedAt"], 110 | } 111 | csv_row_json.update( 112 | { 113 | field_name: antibody_json["fields"][field_name]["textValue"] 114 | for field_name in antibody_field_names 115 | } 116 | ) 117 | writer.writerow(csv_row_json) 118 | 119 | 120 | if __name__ == "__main__": 121 | main() 122 | -------------------------------------------------------------------------------- /upload_results/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | requests = "*" 10 | click = "*" 11 | 12 | [requires] 13 | python_version = "3.7" 14 | -------------------------------------------------------------------------------- /upload_results/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "85d29b2cfc5d4e61d41de6948621b329e2e1c6d692edeabc70fd0d8413e298db" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.7" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", 22 | "sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae" 23 | ], 24 | "version": "==2019.3.9" 25 | }, 26 | "chardet": { 27 | "hashes": [ 28 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 29 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 30 | ], 31 | "version": "==3.0.4" 32 | }, 33 | "click": { 34 | "hashes": [ 35 | "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", 36 | "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" 37 | ], 38 | "index": "pypi", 39 | "version": "==7.0" 40 | }, 41 | "idna": { 42 | "hashes": [ 43 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 44 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 45 | ], 46 | "version": "==2.8" 47 | }, 48 | "requests": { 49 | "hashes": [ 50 | "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", 51 | "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" 52 | ], 53 | "index": "pypi", 54 | "version": "==2.22.0" 55 | }, 56 | "urllib3": { 57 | "hashes": [ 58 | "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", 59 | "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" 60 | ], 61 | "version": "==1.25.3" 62 | } 63 | }, 64 | "develop": {} 65 | } 66 | -------------------------------------------------------------------------------- /upload_results/README.md: -------------------------------------------------------------------------------- 1 | This script demonstrates how to upload results in to Benchling from a CSV file. In this example, the CSV contains some fluorescence measurements from a plate reader. Here are the first few rows: 2 | 3 | | Sample | Well | Signal | Mean | CV | Calc. Concentration | Calc. Conc. Mean | Calc. Conc. CV | 4 | | ------ | ---- | ------ | ---- | ----------- | ------------------- | ---------------- | -------------- | 5 | | U001 | A05 | 4195 | 4428 | 7.44154833 | 335.5175194 | 349.9100519 | 5.816956229 | 6 | | U001 | B05 | 4661 | 4428 | 7.44154833 | 364.3025843 | 349.9100519 | 5.816956229 | 7 | | U002 | C05 | 6151 | 6153 | 0.034478998 | 452.6729507 | 452.7595571 | 0.027051867 | 8 | 9 | # Prerequisites 10 | 11 | This script requires the Results feature of Benchling's Notebook application. To define the type of data to upload, you must configure a **result schema** before running the script. In addition, to group the uploaded results together, you need to configure a **run schema**. Here's now: 12 | 13 | - First, [configure the run schema](https://help.benchling.com/results-enterprise/configuration/configure-a-run-schema). You can leave the fields empty (`"fields": []`) 14 | - The run schema page should have a URL like https://example.benchling.com/your_username/assay-schemas/runs/assaysch_i2sX8NGy. Copy the last part of the URL, e.g. `assaysch_i2sX8NGy`, and write it down as the **run schema ID**. You'll need this to configure the result schema and run the script. 15 | - Then, configure the result schema and copy the result schema ID from URL. Follow the instructions [here](https://help.benchling.com/results-enterprise/configuration/configure-a-result-table) and use the following fields, making sure to replace `YOUR_RUN_SCHEMA_ID_HERE` at the bottom with your own schema ID: 16 | 17 | ``` 18 | { 19 | "fields": [ 20 | { 21 | "isMulti": false, 22 | "type": "text", 23 | "displayName": "Sample", 24 | "name": "sample", 25 | "isRequired": true 26 | }, 27 | { 28 | "isMulti": false, 29 | "type": "text", 30 | "displayName": "Well", 31 | "name": "well", 32 | "isRequired": true 33 | }, 34 | { 35 | "isMulti": false, 36 | "type": "float", 37 | "displayName": "Signal", 38 | "name": "signal", 39 | "isRequired": true 40 | }, 41 | { 42 | "isMulti": false, 43 | "type": "float", 44 | "displayName": "Mean", 45 | "name": "mean", 46 | "isRequired": true 47 | }, 48 | { 49 | "isMulti": false, 50 | "type": "float", 51 | "displayName": "CV", 52 | "name": "cv", 53 | "isRequired": true 54 | }, 55 | { 56 | "isMulti": false, 57 | "type": "float", 58 | "displayName": "Calc. Concentration", 59 | "name": "calc_concentration", 60 | "isRequired": true 61 | }, 62 | { 63 | "isMulti": false, 64 | "type": "float", 65 | "displayName": "Calc. Conc. Mean", 66 | "name": "calc_conc_mean", 67 | "isRequired": true 68 | }, 69 | { 70 | "isMulti": false, 71 | "type": "float", 72 | "displayName": "Calc. Conc. CV", 73 | "name": "calc_conc_cv", 74 | "isRequired": true 75 | }, 76 | { 77 | "isMulti": false, 78 | "isRequired": false, 79 | "schemaId": "YOUR_RUN_SCHEMA_ID_HERE", 80 | "type": "assay_run_link", 81 | "name": "run" 82 | } 83 | ] 84 | } 85 | ``` 86 | 87 | # How to run the script 88 | 89 | - First, ask Benchling support to enable API access on your account, and create API credentials. Instructions: https://help.benchling.com/articles/2353570-access-the-benchling-api-enterprise 90 | - Install Python 3 and [Pipenv](https://docs.pipenv.org/en/latest/) 91 | - Install dependencies using `pipenv install` 92 | - Run `pipenv shell` to work in a virtualenv that includes the dependencies. 93 | - Run the script. For example (replace the schema IDs with your own): 94 | 95 | ``` 96 | python upload.py --domain example.benchling.com --api-key $YOUR_API_KEY --run-schema-id assaysch_i2sX8NGy --result-schema-id assaysch_17odsh8E 97 | ``` 98 | 99 | - The script should print the IDs of the uploaded results: 100 | 101 | ``` 102 | {'assayResults': ['275f8882-3864-461e-b3e1-0c10cad9f7a0', '0fd246db-3522-424d-b959-a9f4d5b18c8f', '2899ab99-fe95-4ca4-bedc-9c256b2118d4', '2a63a15c-95f8-4959-a174-90c0764a193f', 'd6de42ef-8355-4231-80b5-e11f59ca8d93', 'fde94c44-523b-4981-a90d-79cba230962f', '57fe160b-8243-4903-80bf-23884af05c09', '189d2746-8a61-42ca-bb6d-25272586dc14', '891e6054-3ec8-4527-b4d8-983752692294', '397a9657-e4e9-4078-aee8-e2ce08c1d193', '6129454d-d5e5-45fa-8027-5bdca57dc539', 'f31ea3aa-6e7e-47ea-870a-41fc98e7c36a', '272cb2d6-94e1-41e6-9202-20595c6a0264', 'a4557a87-14a1-458b-b329-5a9f062e4ad2', 'ee6572a1-ca94-4790-b130-b6e13d016b15', '2e8f49bd-29c2-42bd-bef7-3e1411bfbadf', '7f80973b-3ff6-484c-94ab-877ea6021ffa', '15728c0b-22e2-427a-9999-98dc6546685b', 'f807db65-c6da-4594-a1e2-ba83ea7e510a', 'cbf7c9f6-ccb8-4aff-84f1-e9b5c34ad053', '0bfa335a-096b-4efb-a72b-ff47bff74c8f', '8a484c3b-9b2d-4b33-8d8b-c3909f5c816e', 'fdc7ecab-e800-4262-8cca-a18ae4a0a903', '213daac9-9c8f-4d59-8c8e-f63927ca9d7f'], 'errors': None} 103 | ``` 104 | 105 | # How to view the results 106 | 107 | After running the script, the results can be viewed in any Notebook entry. 108 | 109 | - Open the Insert menu and select the option for `Run` 110 | 111 | ![Run](images/insert-run.png) 112 | 113 | - Insert an `Example Plate Reader Run` 114 | 115 | ![Example Plate Reader Run](images/example-plate-reader-run.png) 116 | 117 | - Choose `Insert from inbox` 118 | 119 | ![Insert from inbox](images/insert-from-inbox.png) 120 | 121 | - Insert the most recent run 122 | - The results you uploaded should show up: 123 | 124 | ![Results](images/results.png) 125 | -------------------------------------------------------------------------------- /upload_results/images/api-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benchling/integration-examples/79b192a5ebe20373734b1f888f26f612b8f2f29f/upload_results/images/api-key.png -------------------------------------------------------------------------------- /upload_results/images/example-plate-reader-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benchling/integration-examples/79b192a5ebe20373734b1f888f26f612b8f2f29f/upload_results/images/example-plate-reader-run.png -------------------------------------------------------------------------------- /upload_results/images/insert-from-inbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benchling/integration-examples/79b192a5ebe20373734b1f888f26f612b8f2f29f/upload_results/images/insert-from-inbox.png -------------------------------------------------------------------------------- /upload_results/images/insert-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benchling/integration-examples/79b192a5ebe20373734b1f888f26f612b8f2f29f/upload_results/images/insert-run.png -------------------------------------------------------------------------------- /upload_results/images/results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benchling/integration-examples/79b192a5ebe20373734b1f888f26f612b8f2f29f/upload_results/images/results.png -------------------------------------------------------------------------------- /upload_results/images/user-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benchling/integration-examples/79b192a5ebe20373734b1f888f26f612b8f2f29f/upload_results/images/user-menu.png -------------------------------------------------------------------------------- /upload_results/plate_reader_data.csv: -------------------------------------------------------------------------------- 1 | Sample,Well,Signal,Mean,CV,Calc. Concentration,Calc. Conc. Mean,Calc. Conc. CV 2 | U001,A05,4195,4428,7.44154833,335.5175194,349.9100519,5.816956229 3 | U001,B05,4661,4428,7.44154833,364.3025843,349.9100519,5.816956229 4 | U002,C05,6151,6153,0.034478998,452.6729507,452.7595571,0.027051867 5 | U002,D05,6154,6153,0.034478998,452.8461635,452.7595571,0.027051867 6 | U003,E05,8706,9253,8.360259577,595.1458527,624.4324779,6.632829651 7 | U003,F05,9800,9253,8.360259577,653.7191031,624.4324779,6.632829651 8 | U004,G05,8066,8731,10.76393004,560.2871992,596.2299823,8.525363188 9 | U004,H05,9395,8731,10.76393004,632.1727655,596.2299823,8.525363188 10 | U005,A06,7610,7992,6.75120408,535.1452638,556.1065795,5.330592752 11 | U005,B06,8373,7992,6.75120408,577.0678953,556.1065795,5.330592752 12 | U006,C06,6150,3105,138.6885764,452.6152094,227.4277309,140.0283004 13 | U006,D06,60,3105,138.6885764,2.240252292,227.4277309,140.0283004 14 | U007,E06,12998,13512,5.374670942,819.3408588,845.2652195,4.337405788 15 | U007,F06,14025,13512,5.374670942,871.1895803,845.2652195,4.337405788 16 | U008,G06,3419,3156,11.80938912,285.9860942,268.4609318,9.232003381 17 | U008,H06,2892,3156,11.80938912,250.9357695,268.4609318,9.232003381 18 | U009,A07,6236,6384,3.278565276,457.5742353,466.0578712,2.574288234 19 | U009,B07,6532,6384,3.278565276,474.5415071,466.0578712,2.574288234 20 | U010,C07,7485,7589,1.928854236,528.2053323,533.9458529,1.520439198 21 | U010,D07,7692,7589,1.928854236,539.6863734,533.9458529,1.520439198 22 | U011,E07,5025,5446,10.93249926,386.3665626,411.2824208,8.5674327 23 | U011,F07,5867,5446,10.93249926,436.198279,411.2824208,8.5674327 24 | U012,G07,5569,5521,1.229528183,418.7391626,415.9070792,0.962996519 25 | U012,H07,5473,5521,1.229528183,413.0749958,415.9070792,0.962996519 -------------------------------------------------------------------------------- /upload_results/requirements.txt: -------------------------------------------------------------------------------- 1 | -i https://pypi.org/simple 2 | certifi==2019.3.9 3 | chardet==3.0.4 4 | click==7.0 5 | idna==2.8 6 | requests==2.22.0 7 | urllib3==1.25.3 8 | -------------------------------------------------------------------------------- /upload_results/upload.py: -------------------------------------------------------------------------------- 1 | # This script parses a .csv file of results, and creates corresponding 2 | # result objects in Benchling. 3 | import csv 4 | import json 5 | import sys 6 | 7 | import click 8 | import requests 9 | 10 | 11 | def api_get(domain, api_key, path): 12 | url = "https://{}/api/v2/{}".format(domain, path) 13 | rv = requests.get(url, auth=(api_key, "")) 14 | if rv.status_code >= 400: 15 | raise Exception( 16 | "Server returned status {}. Response:\n{}".format( 17 | rv.status_code, json.dumps(rv.json()) 18 | ) 19 | ) 20 | return rv.json() 21 | 22 | 23 | def api_post(domain, api_key, path, body): 24 | url = "https://{}/api/v2/{}".format(domain, path) 25 | rv = requests.post(url, json=body, auth=(api_key, "")) 26 | if rv.status_code >= 400: 27 | raise Exception( 28 | "Server returned status {}. Response:\n{}".format( 29 | rv.status_code, json.dumps(rv.json()) 30 | ) 31 | ) 32 | return rv.json() 33 | 34 | 35 | @click.command() 36 | @click.option( 37 | "--domain", 38 | help="Domain name of your Benchling instance, e.g. example.benchling.com", 39 | required=True, 40 | ) 41 | @click.option("--api-key", help="Your API key", required=True) 42 | @click.option("--run-schema-id", help="ID of run schema", required=True) 43 | @click.option("--result-schema-id", help="ID of result schema", required=True) 44 | def main(domain, api_key, run_schema_id, result_schema_id): 45 | csv_results = [] 46 | # "plate_reader_data.csv" is the name of our example .csv file 47 | # This can be changed to match the name of the .csv file you are using 48 | with open("plate_reader_data.csv") as csvfile: 49 | reader = csv.DictReader(csvfile) 50 | for row in reader: 51 | csv_results.append(dict(row)) 52 | 53 | for csv_result in csv_results: 54 | assert set(csv_result.keys()) == set( 55 | # The following are the headings of the columns in our example .csv file 56 | # This can be changed to match the headings in the .csv file you are using 57 | [ 58 | "Sample", 59 | "Well", 60 | "Signal", 61 | "Mean", 62 | "CV", 63 | "Calc. Concentration", 64 | "Calc. Conc. Mean", 65 | "Calc. Conc. CV", 66 | ] 67 | ) 68 | 69 | response = api_post( 70 | domain, 71 | api_key, 72 | "assay-runs", 73 | {"assayRuns": [{"schemaId": run_schema_id, "fields": {}}]}, 74 | ) 75 | [run_id] = response["assayRuns"] 76 | 77 | response = api_post( 78 | domain, 79 | api_key, 80 | "assay-results", 81 | { 82 | "assayResults": [ 83 | { 84 | "schemaId": result_schema_id, 85 | # This maps columns in the results .csv file to fields in a Benchling result object 86 | # The following is made for the .csv file and results schema used in our example 87 | # These can be changed to match the fields of the results schema you are using 88 | # The keys on the left must match the field name in Benchling 89 | # The keys on the right (being passed into csv_result) must match the column headings 90 | # in your .csv file 91 | "fields": { 92 | "run": run_id, 93 | "sample": csv_result["Sample"], 94 | "well": csv_result["Well"], 95 | "signal": float(csv_result["Signal"]), 96 | "mean": float(csv_result["Mean"]), 97 | "cv": float(csv_result["CV"]), 98 | "calc_concentration": float(csv_result["Calc. Concentration"]), 99 | "calc_conc_mean": float(csv_result["Calc. Conc. Mean"]), 100 | "calc_conc_cv": float(csv_result["Calc. Conc. CV"]), 101 | }, 102 | } 103 | for csv_result in csv_results 104 | ] 105 | }, 106 | ) 107 | print(response) 108 | 109 | 110 | if __name__ == "__main__": 111 | main() 112 | --------------------------------------------------------------------------------