Environment Variables

Make sure that you’ve installed FreeGenes before moving forward.

Token

First you need to find your token! If you login to FreeGenes and go to your user profile (usually located at https://<freegenes-node>/u/profile you’ll see your API token. Before you open an interactive shell (or run a script) you can export your token to the environment:

export FREEGENES_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Base

By default, since we only have a development server running, FreeGenes will be looked for at https://freegenes.dev. When production is deployed, we might change this to https://freegenes.org, and then you would need to optionally specify the development server. For local development, you might want to change this to http://127.0.0.1. You can do that easily by exporting FREEGENES_BASE:

export FREEGENES_BASE=http://127.0.0.1

Both the API token and base URL are tested on instantiation of the client, so make sure you get them right!

Instantiate Client

Once in python, you can import the Client.

from freegenes.main import Client

If you use the defaults and have exported your token, no additional input variables are required - the token will be discovered.

> client = Client()

If you need to provide the base and/or token to the client, you can do as follows:

> client = Client(token='xxxxxxxxxxxxxx', base='https://freegenes.dev'

When you create it, you can inspect it to see the version:

> client                                                                  
[client][freegenes][0.0.0]

Client Shell

The command line FreeGenes also offers a “shell” command that will get you started with a client. You again need to export the environment variables that you might need. If you don’t:

$ freegenes shell
ERROR You must provide a token or export FREEGENES_TOKEN.

When you do:

export FREEGENES_TOKEN=xxxxxxxxxxxx
export FREEGENES_BASE=http://127.0.0.1
$ freegenes shell

Python 3.7.3 (default, Mar 27 2019, 22:11:17) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: client                                                                                                                            
Out[1]: [client][freegenes][0.0.0]

You can use tab completion (with ipython) to see all the functions provided by the client:

In [1]: client. 
  	base                    create_composite_part() create_institution()     
  	cache                   create_container()      create_module()          
  	create_author()         create_distribution()   create_operation()      >
  	create_collection()     create_entity()         create_order()     

These are endpoints, explained in further detail below.

Get Endpoints

A basic endpoint is a function to get a single or list of entities. This contrasts to a more complicated function that might perform a specific task for the lab possibly using multiple endpoints, and performing additional tasks. For functions, see the functions section below.

Let’s explore all of the basic endpoints. Generally, each model in FreeGenes (e.g., a Plate or Protocol) has an endpoint to get details for a single entity, or to list many (paginated) entities. In the case of more complex models, the detail view will have more fields exposed than the listing. For example:

# Get a list, or single author
> client.get_authors()
> client.get_authors(uuid="xxxxxxxxxxx")

> client.get_containers()
> client.get_containers(uuid="xxxxxxxxxxx")

The above works for all of the following:

> client.get_distributions()
> client.get_collections()
> client.get_composite_parts()
> client.get_institutions()
> client.get_modules()
> client.get_operations()
> client.get_orders()
> client.get_organisms()
> client.get_parts()
> client.get_plans()
> client.get_plates()
> client.get_protocols()
> client.get_robots()
> client.get_samples()
> client.get_schemas()
> client.get_tags()

Delete Endpoints

Each of models has a delete function, and it’s also required to be staff or superuser user to use it.

> client.delete_distribution(uuid)
> client.delete_collection(uuid)
> client.delete_composite_part(uuid)
> client.delete_institution(uuid)
> client.delete_module(uuid)
> client.delete_operation(uuid)
> client.delete_order(uuid)
> client.delete_organism(uuid)
> client.delete_part(uuid)
> client.delete_plan(uuid)
> client.delete_plate(uuid)
> client.delete_protocol(uuid)
> client.delete_robot(uuid)
> client.delete_sample(uuid, data)
> client.delete_schema(uuid, data)
> client.delete_tag(uuid, data)

Note that you are required to provide a unique id for the model to delete. A response with status 204 (no content) indicates success.

Update and Patch Endpoints

An update coincides with “POST” and requires that you provide all of the model’s required fields. If you want to update only a substet of fields, then you want to use the patch functions, which coincide with “PATCH.” Patch endpoints include:

> client.patch_distribution(uuid, data)
> client.patch_collection(uuid, data)
> client.patch_composite_part(uuid, data)
> client.patch_institution(uuid, data)
> client.patch_module(uuid, data)
> client.patch_operation(uuid, data)
> client.patch_order(uuid, data)
> client.patch_organism(uuid, data)
> client.patch_part(uuid, data)
> client.patch_plan(uuid, data)
> client.patch_plate(uuid, data)
> client.patch_protocol(uuid, data)
> client.patch_robot(uuid, data)
> client.patch_sample(uuid, data)
> client.patch_schema(uuid, data)
> client.patch_tag(uuid, data)

For each function above, “data” should be a dictionary of parameters that you want to update. See the examples below for how to use patch.

Errors

If you get a bad request, try looking at the json response to determine why:

collection = client.create_collection(name=name, description=description)                                   
ERROR Error with /api/collections/, return value 400: Bad Request

> collection.reason                                                                                           
# 'Bad Request'

> collection.json()
# {'name': ['collection with this name already exists.']}

If you get a 400 family of errors, this indicates Permission denied, and typically results because you need to be staff or superuser to use endpoints that modify data.

Examples

Create an Author

You can create an author as follows:

name = "Big Bird"
email = "yellowisgreat@bird.dev"
affiliation = "Sesame Street"

author = client.create_author(name=name, email=email, affiliation=affiliation)

Here is a successful response, and note that you can also provide an orcid id, although it’s not required.

> author
{'uuid': 'e246f6bb-5cc4-4cc3-bd41-73d24567c0f4',
 'name': 'Big Bird',
 'email': 'yellowisgreat@bird.dev',
 'affiliation': 'Sesame Street',
 'orcid': None,
 'tags': [],
 'label': 'author'}

Update an Author

Oh no, we make a mistake! Let’s update the author we just made. Since we don’t want to provide all fields, we are going to use patch.

author = client.patch_author(uuid=author['uuid'], data={"name": "Notsobig Bird"})
{'uuid': 'f176b56a-bd27-42d5-8867-0d53f09e54e3',
 'name': 'Notsobig Bird',
 'email': 'yellowisgreat@bird.dev',
 'affiliation': 'Sesame Street',
 'orcid': None,
 'tags': [],
 'label': 'author'}

Create a Container

name = "Crayon Box"
container_type = "trash"
description = "This is a crayon trash box?"
parent_id = "1ca52bd5-05e6-4f4f-913b-6760bed611f2"

container = client.create_container(name=name, container_type=container_type, description=description, parent_id=parent_id)

Note that since the top level parent container is the lab (only one per instance), you are required to provide a parent id. Here is the created container:

> container
{'uuid': 'a5541413-b002-4914-8d17-c2d8a30bc962',
 'time_created': '2019-10-06T11:47:39.464202-05:00',
 'time_updated': '2019-10-06T11:47:39.464250-05:00',
 'name': 'Crayon Box',
 'container_type': 'trash',
 'description': 'This is a crayon trash box?',
 'estimated_temperature': None,
 'x': None,
 'y': None,
 'z': None,
 'parent': '1ca52bd5-05e6-4f4f-913b-6760bed611f2',
 'plates': [],
 'label': 'container'}

Update a Container

Let’s update the container above.

container = client.patch_container(uuid=container['uuid'], data={'name': 'Marker Box'})
{'uuid': '6f39b0ae-ca9c-4b7c-8d71-071283349852',
 'time_created': '2019-10-09T09:38:30.590297-05:00',
 'time_updated': '2019-10-09T09:39:14.051493-05:00',
 'name': 'Marker Box',
 'container_type': 'trash',
 'description': 'This is a crayon trash box?',
 'estimated_temperature': None,
 'x': None,
 'y': None,
 'z': None,
 'parent': '763221b0-8bdd-4b23-8da0-95eedce877ba',
 'plates': [],
 'label': 'container'}

Create a Distribution

A distribution requires a name and description

name = "Send out the hounds"
description = "So many hounds"
plateset_ids = ["37d895e3-eaf1-48f5-ad3d-9a7571e4434c"]

dist = client.create_distribution(name=name, description=description, plateset_ids=plateset_ids)

Here is the created distribution:

> dist
{'uuid': '418f1e66-7f3b-46cf-b707-40bad407f202',
 'time_created': '2019-10-06T11:53:20.121227-05:00',
 'time_updated': '2019-10-06T11:53:20.121277-05:00',
 'name': 'Send out the hounds',
 'description': 'So many hounds',
 'platesets': ['37d895e3-eaf1-48f5-ad3d-9a7571e4434c'],
 'label': 'distribution'}

Note that you can provide a list of plateset ids, or a single id for platesets.

Update a Distribution

Update the distribution as follows:

dist = client.patch_distribution(uuid=dist['uuid'], data={"name": "Send in the hounds"})
{'uuid': '418f1e66-7f3b-46cf-b707-40bad407f202',
 'time_created': '2019-10-06T11:53:20.121227-05:00',
 'time_updated': '2019-10-06T11:53:20.121277-05:00',
 'name': 'Send in the hounds',
 'description': 'So many hounds',
 'platesets': ['37d895e3-eaf1-48f5-ad3d-9a7571e4434c'],
 'label': 'distribution'}

Create a Collection

A collection requires a name and description, and a parent_id is required.

name="dinosaur-collection"                                                                                  
description="This is the description"
parent_id = '763221b0-8bdd-4b23-8da0-95eedce877ba'                      

collection = client.create_collection(name=name, description=description, parent_id=parent_id)

If you are superuser or admin, the created collection will be returned.

{'uuid': 'ec624409-26cb-4882-969e-f0d7c1c1aa44',
 'time_created': '2019-10-06T10:01:36.935007-05:00',
 'time_updated': '2019-10-06T10:01:36.935073-05:00',
 'name': 'dinosaur-collection',
 'description': 'This is the description',
 'parent': None,
 'tags': [],
 'label': 'collection'}

You can also provide a parent collection id with parent_id or a list of tag ids with “tag_ids”.

Update a Collection

To update a collection:

collection = client.patch_collection(uuid=collection['uuid'], data={"name": "avocado-collection"})
{'uuid': 'ec624409-26cb-4882-969e-f0d7c1c1aa44',
 'time_created': '2019-10-06T10:01:36.935007-05:00',
 'time_updated': '2019-10-06T10:01:36.935073-05:00',
 'name': 'avocado-collection',
 'description': 'This is the description',
 'parent': None,
 'tags': [],
 'label': 'collection'}

Create a Module

To create a module, you need to provide more fields:

name = "Dinosaur module"
notes = "This is a module for dinosaurs."
model_id = "dino-raptor-3000"
module_type = "pipette"
container_id = "1ca52bd5-05e6-4f4f-913b-6760bed611f2"

module = client.create_module(name=name, notes=notes, model_id=model_id, module_type=module_type, container_id=container_id)

Note that the container id is the uuid for an existing container in the database. A successful response looks like the following:

> module
{'uuid': '1b46bb05-09c9-401c-9f32-b1a17240a3ba',
 'time_created': '2019-10-06T11:34:26.698264-05:00',
 'time_updated': '2019-10-06T11:34:26.698314-05:00',
 'name': 'Dinosaur module',
 'container': '1ca52bd5-05e6-4f4f-913b-6760bed611f2',
 'notes': 'This is a module for dinosaurs.',
 'model_id': 'dino-raptor-3000',
 'module_type': 'pipette',
 'data': {},
 'label': 'module'}

Update a Module

To update a module:

module = client.patch_module(uuid=module['uuid'], data={"name": "Avocado Module"})
> module
{'uuid': '1b46bb05-09c9-401c-9f32-b1a17240a3ba',
 'time_created': '2019-10-06T11:34:26.698264-05:00',
 'time_updated': '2019-10-06T11:34:26.698314-05:00',
 'name': 'Avocado Module',
 'container': '1ca52bd5-05e6-4f4f-913b-6760bed611f2',
 'notes': 'This is a module for dinosaurs.',
 'model_id': 'dino-raptor-3000',
 'module_type': 'pipette',
 'data': {},
 'label': 'module'}

Create an Institution

An institution only required a name, and a boolean to indicate if they have signed a master MTA agreement (defaults to False):

name = "Dinosaur College"
signed_master = True

institution = client.create_institution(name=name, signed_master=signed_master)

A successful response looks like the following:

{'uuid': 'b8ebf93f-5418-4a19-b2ab-1b6737ba1142',
 'name': 'Dinosaur College',
 'signed_master': True,
 'label': 'institution'}

Update an Institution

institution = client.patch_institution(uuid=institution['uuid'], data={"name": "Lizard School"})
{'uuid': 'cf7176db-38ef-4188-8f99-27458da7558a',
 'name': 'Lizard School',
 'signed_master': True,
 'label': 'institution'}

Create an Operation

An operation requires a name and description.

name = "Operation Neptune"
description = "pew pew pew"

operation = client.create_operation(name=name, description=description)

A successful operation looks like the following:

> operation

{'uuid': '9374b582-8237-4939-8ff6-0fbdce691724',
 'time_created': '2019-10-06T11:37:45.301572-05:00',
 'time_updated': '2019-10-06T11:37:45.301623-05:00',
 'name': 'Operation Neptune',
 'description': 'pew pew pew',
 'plans': [],
 'label': 'operation'}

Note that you can also provide a list of plan_ids to select plans.

Update an Operation

operation = client.patch_operation(uuid=operation['uuid'], data={"name": "Operation Venus"})

Create an Order

Orders require a name and a description.

name = "Buy Taco Genes"
notes = "Extra avocado please"

order = client.create_order(name=name, notes=notes)

A successful response looks like the following:

> order
{'uuid': 'b43a53e8-d957-434d-8553-ab7384e0db1f',
 'time_created': '2019-10-06T11:40:39.715394-05:00',
 'time_updated': '2019-10-06T11:40:39.715426-05:00',
 'name': 'Buy Taco Genes',
 'notes': 'Extra avocado please',
 'distributions': [],
 'label': 'order'}

You can also provide distribution_ids to add one or more distributions to the order (a list).

Update an Order

order = client.patch_order(uuid=order['uuid'], data={'notes': 'Extra avocado AND beans'})

Create an Organism

An organism requires a name, description, and genotype.

name = "Vanessasaurus"
description = "A soon to be extinct dinosaur."	
genotype = "Gaaattaagaataata"

organism = client.create_organism(name=name, description=description, genotype=genotype)

Here is a successful response:

> organism
{'uuid': 'bafb3e9a-7bd0-40fc-a402-f17793e4ff05',
 'time_created': '2019-10-06T12:29:24.371688-05:00',
 'time_updated': '2019-10-06T12:29:24.371721-05:00',
 'name': 'Vanessasaurus',
 'description': 'A soon to be extinct dinosaur.',
 'genotype': 'Gaaattaagaataata',
 'label': 'organism'}

Update an Organism

organism = client.patch_organism(uuid=organism['uuid'], data={"description": "An extinct dinosaur"})

Create a Part

A part requires the following fields:

kwargs = {
    "name": "Thread",
    "description": "a piece of twine",
    "gene_id": "twiney-123",
    "part_type": "plasmid",
    "primer_forward": "AATG",
    "primer_reverse": "TGAA",	
    "author_id": "c9d9237b-c4f7-48a9-9ac1-33c2393cfbf1"
}

part = client.create_part(**kwargs)

A successful response looks like the following:

> part
{'uuid': 'e0fd70fe-3c82-4b48-83f2-ac2425a9e177',
 'time_created': '2019-10-06T12:42:35.703039-05:00',
 'time_updated': '2019-10-06T12:42:35.703081-05:00',
 'name': 'Thread',
 'description': 'a piece of twine',
 'status': 'null',
 'gene_id': 'twiney-123',
 'part_type': 'plasmid',
 'genbank': {},
 'original_sequence': None,
 'optimized_sequence': None,
 'synthesized_sequence': None,
 'full_sequence': None,
 'vector': None,
 'primer_forward': 'AATG',
 'primer_reverse': 'TGAA',
 'barcode': None,
 'label': 'part',
 'translation': None,
 'tags': [],
 'collections': [],
 'author': 'c9d9237b-c4f7-48a9-9ac1-33c2393cfbf1'}

Update a Part

part = client.patch_part(uuid=part['uuid'], data={'name': 'Thimble'})

Create Composite Part

You can define a new composite part based on a new sequence, which should be a combination of parts (in forward or reverse directions) from the current database. You are required to provide a name, and sequence. For example, here is a composite part derived from two parts, one forward, and one reversed:

name = "Dinosaur Part"
sequence = 'ATGGCTGCAAATAATAAAAAGTATTTTCTGGAGAGCTTTAGCCCGCTGGGTTATGTTAAGAACAACTTTCAGGGTAATCTGCGTAGCGTTAATTGGAATCTGGTTGACGACGAAAAAGATCTGGAGGTGTGGAATCGCATCGTGCAGAATTTCTGGCTGCCGGAAAAAATTCCGGTTAGCAATGACATCCCGAGCTGGAAGAAACTGAGCAAAGACTGGCAGGATCTGATCACCAAAACCTTTACCGGCCTGACCCTGCTGGACACCATCCAGGCGACGATTGGTGACATCTGCCAAATCGATCACGCGCTGACCGATCACGAACAAGTTATCTACGCGAACTTTGCCTTCATGGTCGGTGTGCACGCACGTTCCTACGGTACCATCTTTAGCACCTTGTGCACGTCAGAGCAAATCAATGCCGCGCACGAATGGGTGGTCAACACCGAGAGCCTGCAAAAGCGCGCTAAGGCACTGATTCCTTACTACACTGGTAACGACCCACTGAAATCCAAAGTCGCGGCGGCGCTGATGCCGGGCTTCTTGCTGTATGGTGGTTTCTATCTGCCGTTTTACCTGTCCAGCCGTAAGCAGCTCCCGAACACGTCGGACATCATTCGTCTGATCCTGCGTGATAAGGTTATCCATAATTACTATAGCGGTTATAAATACCAACGCAAGCTGGAAAAACTGCCTCTTGCAAAACAAAAAGAGATGAAGGCCTTCGTTTTTGAGCTGATGTATCGCCTGATTGAGCTGGAGAAAGATTATCTGAAGGAACTGTACGAGGGTTTCGGCATCGTGGACGACGCCATCAAGTTCAGCGTTTATAATGCTGGTAAATTTCTGCAGAATCTGGGCTACGATAGCCCGTTTACGGCAGCGGAAACCCGTATCAAACCGGAGATCTTCGCGCAACTGAGCGCGCGTGCAGACGAGAACCACGACTTCTTTTCCGGTAATGGTTCTTCGTACGTTATGGGCGTTTCTGAGGAAACGAACGATGACGACTGGAATTTTTGAAGTCCGGTTCTCAAGGTTTAGGAATGCTTAGTTCAAGAGAAAGAACTATTTTTAGAACAGAAAAAAGTAAAGGTATTTGCAGCCTTACAATCGTTTGTCCCAGTAGTCGTCCTAACGGTGCATCGAGTCTCTTTTCGGGTGTTACTTGTGGTCAACGCCTAGCGATAACGAGTCTAACTCTTTCCAGCGTTGCAAGAACATGAGTGGTAAGCAGGTCAGCGAGCGGTCCTATGGTCACCATAACGGCCATTAAACTGGGAATTTCTGGCTTTGGTAATTACGGTGCATCCTCTTTATCTAGTCGCGCTTTTGCACCTACATCTTTTTTCTTTAGTCCGACTTCGTCAATAGGGTGTCCTACTAGCGGTCCGAGTGGTCGTCCCTGTCCTTCACGTAGTACTTTATCTAAACCAATAACGGCCATGGTTGTTTTTATTTCAACTGTTACAGAACGAGTAACTGGCCCCATTTCTAGTCGAGTGGTTTGTCGCACTGGTCGCGCGGCTTGTCTGGTTTTTGGCGGTCGTGTGGTCTTTAAAAGAAGACGAAGTCCTTTTGCTTTTGGCCTCGTTAGTGCCGTTTCTTACCCGTGTCCAGGTTGTCTTTCTACGCCGAGTCTGCGTCCGATAGGTCTAAAACCGACGAGACCGATAAAACTGGTATCTGTAGTCCTTTGTCTATTTATTAGACCGCCGGCTTTTTCTGCATCTGTTTGTCCTGCTACCGGTCCTGAACTTAGTTTATTTTTGCTTAGAAGTCGAACGCCTGGTCGCCGCATTTGCGGTCCTGCGATTATTGTGGCCGCTATTAGTCCTATTAGTGTTCTATTAAGTCGCAAACGTA'
> composite_part = client.create_composite_part(name=name, sequence=sequence)
Caching parts for future requests...

The way this works, the client caches all parts from the server at the onset, that way you can do it once and then use the cache as many times as you need. We first look for all forward and reverse sequences for each part in your new part, and then from those results we find a “best answer” with a scheduling algorithm:

  1. The list of matching parts (and directions) is sorted based on length
  2. We create a queue, and process it by popping one off the end (longest) until the queue is empty.
  3. For each new element, we check it against a list of selected_sequence items (which starts empty) to look for any overlap.
  4. If there isn’t overlap, we add to selected sequences.

We assume that the sequence provided is circular, but if it’s not, you should set circular to False:

> composite_part = client.create_composite_part(name=name, sequence=sequence, circular=False)

The final result will include an ordered list of the longest parts. If there are other combinations, the client could remove the first match (the longest) and repeat the algorithm again - however in practice the first result is the “right answer” and subsequent results turn out to be repeats of incredibly tiny optimized sequences (e.g., TGA). This isn’t implemented, but could be - please open an issue to discuss if this is needed.

Once we have an ordered list of part ids, directions, and the name, we can make the request to the server to create the Composite Part. If the create or update is successful you’ll get the complete part back:

{'uuid': '9c4b6c9a-b929-4bc8-8b96-38064f9ecbd9',
 'time_created': '2019-09-28T09:52:56.871439-05:00',
 'time_updated': '2019-09-28T09:52:56.871491-05:00',
 'name': 'Dinosaur Part',
 'description': None,
 'composite_id': None,
 'composite_type': None,
 'direction_string': '><',
 'sequence': 'ATGGCTGCAAATAATAAAAAGTATTTTCTGGAGAGCTTTAGCCCGCTGGGTTATGTTAAGAACAACTTTCAGGGTAATCTGCGTAGCGTTAATTGGAATCTGGTTGACGACGAAAAAGATCTGGAGGTGTGGAATCGCATCGTGCAGAATTTCTGGCTGCCGGAAAAAATTCCGGTTAGCAATGACATCCCGAGCTGGAAGAAACTGAGCAAAGACTGGCAGGATCTGATCACCAAAACCTTTACCGGCCTGACCCTGCTGGACACCATCCAGGCGACGATTGGTGACATCTGCCAAATCGATCACGCGCTGACCGATCACGAACAAGTTATCTACGCGAACTTTGCCTTCATGGTCGGTGTGCACGCACGTTCCTACGGTACCATCTTTAGCACCTTGTGCACGTCAGAGCAAATCAATGCCGCGCACGAATGGGTGGTCAACACCGAGAGCCTGCAAAAGCGCGCTAAGGCACTGATTCCTTACTACACTGGTAACGACCCACTGAAATCCAAAGTCGCGGCGGCGCTGATGCCGGGCTTCTTGCTGTATGGTGGTTTCTATCTGCCGTTTTACCTGTCCAGCCGTAAGCAGCTCCCGAACACGTCGGACATCATTCGTCTGATCCTGCGTGATAAGGTTATCCATAATTACTATAGCGGTTATAAATACCAACGCAAGCTGGAAAAACTGCCTCTTGCAAAACAAAAAGAGATGAAGGCCTTCGTTTTTGAGCTGATGTATCGCCTGATTGAGCTGGAGAAAGATTATCTGAAGGAACTGTACGAGGGTTTCGGCATCGTGGACGACGCCATCAAGTTCAGCGTTTATAATGCTGGTAAATTTCTGCAGAATCTGGGCTACGATAGCCCGTTTACGGCAGCGGAAACCCGTATCAAACCGGAGATCTTCGCGCAACTGAGCGCGCGTGCAGACGAGAACCACGACTTCTTTTCCGGTAATGGTTCTTCGTACGTTATGGGCGTTTCTGAGGAAACGAACGATGACGACTGGAATTTTTGAAGTCCGGTTCTCAAGGTTTAGGAATGCTTAGTTCAAGAGAAAGAACTATTTTTAGAACAGAAAAAAGTAAAGGTATTTGCAGCCTTACAATCGTTTGTCCCAGTAGTCGTCCTAACGGTGCATCGAGTCTCTTTTCGGGTGTTACTTGTGGTCAACGCCTAGCGATAACGAGTCTAACTCTTTCCAGCGTTGCAAGAACATGAGTGGTAAGCAGGTCAGCGAGCGGTCCTATGGTCACCATAACGGCCATTAAACTGGGAATTTCTGGCTTTGGTAATTACGGTGCATCCTCTTTATCTAGTCGCGCTTTTGCACCTACATCTTTTTTCTTTAGTCCGACTTCGTCAATAGGGTGTCCTACTAGCGGTCCGAGTGGTCGTCCCTGTCCTTCACGTAGTACTTTATCTAAACCAATAACGGCCATGGTTGTTTTTATTTCAACTGTTACAGAACGAGTAACTGGCCCCATTTCTAGTCGAGTGGTTTGTCGCACTGGTCGCGCGGCTTGTCTGGTTTTTGGCGGTCGTGTGGTCTTTAAAAGAAGACGAAGTCCTTTTGCTTTTGGCCTCGTTAGTGCCGTTTCTTACCCGTGTCCAGGTTGTCTTTCTACGCCGAGTCTGCGTCCGATAGGTCTAAAACCGACGAGACCGATAAAACTGGTATCTGTAGTCCTTTGTCTATTTATTAGACCGCCGGCTTTTTCTGCATCTGTTTGTCCTGCTACCGGTCCTGAACTTAGTTTATTTTTGCTTAGAAGTCGAACGCCTGGTCGCCGCATTTGCGGTCCTGCGATTATTGTGGCCGCTATTAGTCCTATTAGTGTTCTATTAAGTCGCAAACGTA',
 'parts': ['81a92bdc-2b71-48de-bdfc-fafcf9bf26ed',
  'e7d46d00-e32e-417b-8628-0f5287d55840'],
 'label': 'compositepart'}

See this original issue for discussion about this endpoint function.

Update a Composite Part

This is the one slightly different patch function, because if you provide a sequence it will calculate updated parts, and if you provide just parts, you are also required to provide the corresponding sequence. The circular argument is also provided as a boolean.

composite_part = client.patch_composite_part(uuid=composite_part['uuid'], data={'sequence': sequence}, circular=False)

Create a Plan

name = "Dr. Evil's Plan"
description = "One hundred... billion dollars!"
status = "Planned"
operation_id = '9374b582-8237-4939-8ff6-0fbdce691724'

plan = client.create_plan(name=name, description=description, status=status, operation_id=operation_id)

The successful response looks like the following:

> plan
{'uuid': '1f9bf6ab-0871-4d70-948f-533df87914af',
 'time_created': '2019-10-06T12:49:58.534434-05:00',
 'time_updated': '2019-10-06T12:49:58.534480-05:00',
 'name': "Dr. Evil's Plan",
 'description': 'One hundred... billion dollars!',
 'parent': None,
 'operation': '9374b582-8237-4939-8ff6-0fbdce691724',
 'status': 'Planned',
 'label': 'plan'}

Note you can also provide a parent_id if relevant.

Update a Plan

plan = client.patch_plan(uuid=plan['uuid'], data={"name": "Austin Power's Plan"})

Create a Plate

The following fields are required:

kwargs = {
    "plate_type": "culture",
    "plate_form": "standard96",
    "status": "Planned",
    "name": "Broccoli Fingers",
    "notes": "Broccolini or broccoli?",
    "container_id": "1ca52bd5-05e6-4f4f-913b-6760bed611f2"
}

plate = client.create_plate(**kwargs)

Note that you can also provide well_ids and protocol_id, along with length and width. Here is the created object:

> plate
{'uuid': '8463b8b7-3726-4919-8dac-e8f71c445280',
 'time_created': '2019-10-06T12:56:40.314493-05:00',
 'time_updated': '2019-10-06T12:56:40.314532-05:00',
 'plate_type': 'culture',
 'plate_form': 'standard96',
 'status': 'Planned',
 'name': 'Broccoli Fingers',
 'thaw_count': 0,
 'notes': 'Broccolini or broccoli?',
 'height': 16,
 'length': 24,
 'container': '1ca52bd5-05e6-4f4f-913b-6760bed611f2',
 'protocol': None,
 'wells': [],
 'label': 'plate'}

Update a Plate

plate = client.patch_plate(uuid=plate['uuid'], data={"notes": "Asparagus"})

Create a PlateSet

You are required to provide one or more plates to create a plateset.

description = "The best plates"
name = "My Plates"
plate_ids = ['8463b8b7-3726-4919-8dac-e8f71c445280']

plateset = client.create_plateset(description=description, name=name, plate_ids=plate_ids)

The created object is shown below.

> plateset
{'uuid': '6e12439e-2aff-4e16-9726-390e8d21b198',
 'description': 'The best plates',
 'name': 'My Plates',
 'time_created': '2019-10-06T12:59:11.632197-05:00',
 'time_updated': '2019-10-06T12:59:11.632245-05:00',
 'plates': ['8463b8b7-3726-4919-8dac-e8f71c445280'],
 'label': 'plateset'}

Update a PlateSet

plateset = client.patch_plateset(uuid=plateset['uuid'], data={"name": "Your Plates"})

Create a Sample

You must provide wells and a part unique id:

part_id = "243611b6-124a-461f-bfba-bf745b131db3"
well_ids = ['565ab16f-4fb0-45ec-a296-a620a5cd0d24',
            '0740224f-34d5-45c4-a672-30b5ed6e1472']

sample = client.create_sample(part_id=part_id, well_ids=well_ids)

Here is a successful response:

> sample
{'uuid': 'da03fed5-0d67-498d-9071-22485f479d4f',
 'outside_collaborator': True,
 'sample_type': None,
 'status': None,
 'evidence': None,
 'vendor': None,
 'time_created': '2019-10-06T13:16:53.029585-05:00',
 'time_updated': '2019-10-06T13:16:53.029633-05:00',
 'derived_from': None,
 'part': '243611b6-124a-461f-bfba-bf745b131db3',
 'index_forward': None,
 'index_reverse': None,
 'label': 'sample',
 'wells': ['0740224f-34d5-45c4-a672-30b5ed6e1472',
  '565ab16f-4fb0-45ec-a296-a620a5cd0d24']}

Update a Sample

sample = client.patch_sample(uuid=sample['uuid'], data={"vendor": "Meatball"})

Create a Protocol

You only are required to add a description for a protocol.

description = "What is a Protocol, Protocol"

protocol = client.create_protocol(description=description)

If you want to add “data” (a dictionary) for it, you can specify protocol_data or a schema with schema_id. Here is a response from the above:

> protocol
{'uuid': '9c0dea2c-07fd-4697-86b9-02f7f84647e1',
 'time_created': '2019-10-06T13:00:30.479923-05:00',
 'time_updated': '2019-10-06T13:00:30.479962-05:00',
 'data': {},
 'description': 'What is a Protocol, Protocol',
 'label': 'protocol',
 'schema': None}

Update a Protocol

protocol = client.patch_protocol(uuid=protocol['uuid'], data={'description': 'Over there!'})

Create a Robot

Here is how to create a robot:

kwargs = {
    "container_id": "1ca52bd5-05e6-4f4f-913b-6760bed611f2",
    "name": "Dinobot",
    "robot_id": "dinobot-3000",
    "notes": "This is the dinobot.",
    "server_version": "1.0.0",
    "right_mount_id": '1b46bb05-09c9-401c-9f32-b1a17240a3ba',
    "left_mount_id": '3443ba88-a75e-4f1b-be96-eb185745528e'
}	

robot = client.create_robot(**kwargs)

You can also provide an optional robot_type. Here is the successful response:

> robot
{'uuid': '89d360ed-211b-4a27-8a54-601e4d0ec8ed',
 'time_created': '2019-10-06T13:07:21.996704-05:00',
 'time_updated': '2019-10-06T13:07:21.996914-05:00',
 'container': '1ca52bd5-05e6-4f4f-913b-6760bed611f2',
 'name': 'Dinobot',
 'robot_id': 'dinobot-3000',
 'robot_type': 'OT2',
 'notes': 'This is the dinobot.',
 'server_version': '1.0.0',
 'right_mount': '1b46bb05-09c9-401c-9f32-b1a17240a3ba',
 'left_mount': '3443ba88-a75e-4f1b-be96-eb185745528e',
 'label': 'robot'}

Update a Robot

robot = client.patch_robot(uuid=robot['uuid'], data={"name": "TVBot"})

Create a Schema

Here is how to create a schema:

name = "Robot Schema"
description = "This is the schema for a robot"	
schema_version = "1.0.0"

schema = client.create_schema(name=name, description=description, schema_version=schema_version)

Here is the successful response:

> schema
{'uuid': '0764da51-7ecc-466d-a488-b12a31924162',
 'time_created': '2019-10-06T13:19:05.715045-05:00',
 'time_updated': '2019-10-06T13:19:05.715099-05:00',
 'name': 'Robot Schema',
 'description': 'This is the schema for a robot',
 'schema': {},
 'schema_version': '1.0.0',
 'label': 'schema'}

Update a Schema

schema = client.patch_schema(uuid=schema['uuid'], data={'description': "This is not a schema for a robot"})

Create a Tag

Creating a tag simply requires a tag!

tag = client.create_tag(tag="bogey")

> tag
{'uuid': 'd600c0f6-9ff8-4e3b-9c27-07f5476f54be',
 'tag': 'bogey',
 'label': 'tag'}

Update a Tag

tag = client.patch_tag(uuid=tag['uuid'], data={'tag': 'down-now'})