Package: chemotion-api
ChemotionApiPLEASE NOTICE: This version is far from beeing ready.Open ToDosThe generic elements section is not implementedThe research plan section is not implementedThe admin functions are not implementedCreate dataset function is not implementedShare and Sync functions are not implementedA python api forChemotion. Chemotion is a Electronic Laboratory Notebook (ELN) developed at
the KIT is a web-based platform designed to help researchers manage and organize their experimental data in a secure and
efficient manner.The ChemotionApi Python package is a wrapper for the RESTful API built on top of the Ruby on Rails
framework that allows users to interact with Chemotion ELN. The package provides a simple and intuitive
interface for creating, retrieving, updating, and deleting data stored in the ELN using Python. With
its RESTful architecture and comprehensive documentation, the ChemotionApi allows users to build custom applications and
workflows that leverage the ELN's capabilities and streamline their research processes.InstallationAt the moment this package has not been daploayed toPyPI.To install, simply clone the package to a directory of
your choice:gitclonehttps://github.com/StarmanMartin/ChemotionApi.gitcdChemotionApiTo build and install this package we usePoetry. Hence, if you want to install it poetry
has to be installed on your system (howtohere).
To build it simpy go into the directory ad executepoetryinstall#to install all dependenciespoetrybuild
pipinstall<abspath_to_ChemotionApi_zip># most likely in the dist directoryUsageIn order for the API to work, a connection to a Chmotion instance must first be established.
For this purpose a object of the typeInstancemust be initialized. This object gets the
host URL passed to it as a string.fromchemotion_apiimportInstanceURL="http(s)://xxx.xxx.xxx/"instance=Instance(URL)You can check if the connection works b<y:try:instance.test_connection()except:print('Connection failed!')Almost all functions provided by this API require a successful login first:try:instance.login('<USER>','<PASSWORD>')except:print('login failed!')After the instance is logged in, different functions are available
depending on the user type and rights. Following come the admin
function and then the functions of a standard user.Admin BasicsToDoUser BasicsThe main function offered to standard user are used to read,
edit, create or delete elements such
asSamples,Reactions,Research Plans,Wellplatesand/orGeneric elements.
However, the basic workflow with chemotion divides the elements of the projects into
so-calledcollections.
Hence, most of the element related function are outsourced to a Collection object. Only getting single elements based on
a ID
can be fetched from theinstanceobject. To fetch a Sample:try:sample=instance.get_sample(1)# Let 1 be the id of a sample in this exampleexcept:print('No Sample could be fetched')To Fetch a Reaction:try:reaction=instance.get_reaction(1)# Let 1 be the id of a reaction in this exampleexcept:print('No Sample could be fetched')To Fetch a Wellplate:try:wellplate=instance.get_wellplate(1)# Let 1 be the id of a wellplate in this exampleexcept:print('No Sample could be fetched')To Fetch a Research Plan:try:research_plan=instance.get_research_plan(1)# Let 1 be the id of a research plan in this exampleexcept:print('No Sample could be fetched')In the sectionElementswe show what you can do with the retrieved elements. First, however, we will explain how to use the collections.CollectionsCollections are the main principle in Chemotion to organize your project. To work with the collections, you must always get the
collection root object first:fromchemotion_api.collectionimportRootCollectionroot_collection:RootCollection=instance.get_root_collection()Theroot_collectionobjects enables you to manage all your collections. To address a collection use simple linux path description. The first following code snippet shows how to create an new collection:fromchemotion_api.collectionimportRootCollection,Collectionroot_collection:RootCollection=instance.get_root_collection()# Creates a collection at the path /Col1col_1:Collection=root_collection.get_or_create_collection('/Col1')# Creates a collection at the path /Col1/Col2col_2:Collection=root_collection.get_or_create_collection('Col1/Col2')If the path is absolutei.e.it starts with "/" it creates the collection starting from your root path.
Hence, it makes no difference to use an absolute path or a relative path in yourroot_collectionobject. However,
you can create collections also in another collection object. In this case you should use a relative path otherwise
it again uses theroot_collectionas stating point to create a new collection:# Creates a collection at the path /Col1/Col2/Col3col_3:Collection=col_2.get_or_create_collection('Col3')# Creates a collection at the path /Col4col_4:Collection=col_1.get_or_create_collection('/Col4')To navigate through ypur collections you need to either use theget_or_create_collectionfunction or theget_collectionfunction.
But be aware thatget_collectionwill throw an error if the path does not exist.col_2_a:Collection=root_collection.get_collection('/Col1/Col2')col_2_b:Collection=col_1.get_collection('Col2')try:x=col_1.get_collection('XXX')except:print('The collection "XXX" does not exist!')assertcol_2.id==col_2_a.idassertcol_2.id==col_2_b.idAll collection can be used to get all elements of a type maintained by itself as an ElementSet object as in the following example:fromchemotion_api.elementsimportElementSetper_page=5# To get all samplessamples:ElementSet=root_collection.get_samples(per_page=per_page)# To get all samples in col_1samples_col_1:ElementSet=col_1.get_samples(per_page=per_page)# To get all reactionsreactions:ElementSet=root_collection.get_reactions(per_page=per_page)# To get all wellplateswellplate:ElementSet=root_collection.get_wellplates(per_page=per_page)# To get all research plansresearch_plan:ElementSet=root_collection.get_research_plans(per_page=per_page)The getter in theroot_collectionget all of teh ellements of a type as in the
"All" collection.Please notice that these getter have an optional parameterper_page. Since the system would most likely crash if you would fetch too many elements of a type at once, it fetches only a certain number at the time.
Theper_pageparameter specifies the numer of fetched elements.
To iterate over all Elements you can use the following:with[]asids:forsinsamples.iter_pages():assertlen(s)<=per_pageforsampleins:assertsample.idnotinidsids.append(sample.id)s=samples.prev_page()# Returns previous page if it exists else it has no effectforsampleins:assertsample.idinidsassertlen(s)<=per_pages=samples.next_page()# Returns next page if it exists else it has no effectforsampleins:assertsample.idinidsassertlen(s)<=per_pageYou can rename a collection you need to rename the label attribute of a collection object and save it. To save a collection you have to use thesavefunction of theroot_collection:# Rename the collection to Col1Acol_1.lable+='A'# Save all changesroot_collection.save()Note that from now on the path of all sub-collections of Col1A change to /Col1A/...Collection can also be moved into other collection. To move a collection either use themovefunction of a collection object or use a global move function of theroot_collection:# Move Col4 into Col1Acol_4.move('/Col1A')# Save all changesroot_collection.save()# Move it backroot_collection.move('/Col1A/Col4','/')# Save all changesroot_collection.save()Finally, we explain how you can delete a collection. Each non-root collection can be deleted by itsdeletefunction or thedeletefunction of theroot_collectionwith a path.# To delete Col4col_4.delete()# To delete /Col1/Col2root_collection.delete('/Col1A/Col2')# Save all changesroot_collection.save()ElementsTo interact with the elements (e.g.,Samples,Reactions,Research Plans,Wellplatesand/orGeneric elements),
you can use the corresponding objects obtained from the instance or the collection. Here are some examples how to obtain the objects:# To get an element by its ID use the instance objectsample=instance.get_sample(1)# To get all elements in a collection use the collection object# Set the parameter reload to 'false' to prevent unnecessary data-loadingsamples=instance.get_root_collection(reload=False).get_collection('Col1A').get_samples()SeeUser BasicsorCollectionson how to fetch other types than Samples.
All types of element (samples, reactions, research plans, wellplates, and/or general elements), have some common basic properties.
We will introduce these common properties based on the sample object first. After this introduction of the common functions we go into the individual features of the objects.Each element has thejson_dataobject. It contains the origen data fetched from the server and should NOT be manipulated by you.
Instead, you can use the preprocessed objectsproperties,analysesandsegments. Thepropertiesis a simpledictandanalysesis a list of extendeddictsobjects.
To change simple properties of an element permanently you simply change the value in the dicts and then save the object:WARNING: be careful the data you change will not be validated by the AIP!# The boiling point lowerbound is a simple floatsample.properties['boiling_point_lowerbound']=99.5# The boiling point upperbound is a simple floatsample.properties['boiling_point_upperbound']=100.5# The boiling point upperbound is a system value (it has a value and a unit)sample.properties['target_amount']['value']=5sample.properties['target_amount']['unit']='g'# And we also change the name of the first analyses of the sample (must exist) analysessample.analyses[-1]['name']='My Sample analyses'sample.save()To see all possible properties use something like:importjsonprint(json.dumps(sample.properties))From the analyses objects we can fetch more complex data by using its extended properties such as:ana_1=sample.analyses[-1]# To download the preveiw imagewithopen('<path_to_svg_file>','wb')asf:f.write(ana_1.preview_image())# To get the Dataset objects:fordatasetinana_1.datasets:# Write all as ZIPdataset.write_zip('<path_to_zip_file>')# Write all as xlsxdataset.write_data_set_xlsx('<path_to_xlsx_file>')A dataset is also an extended dict and can therefore be changed by simpley changing ist values:foridx,datasetinenumerate(ana_1.datasets):if'name'indataset:dataset['name']="DS #{}".format(idx)# To commit your changes save the samplesample.save()Next we want to create a new element.
Since any element has to be in a collection you have to call the create new-element function of a collection.# To create a new Samplenew_sample=col_1.new_sample()# To create a new Reactionnew_reaction=col_1.new_reaction()# To create a new Reasearch Plannew_research_plan=col_1.new_research_plan()# To create a new Wellplatenew_wellplate=col_1.new_wellplate()Note that it is not automatically saved. You need to call thesavefunction to finally commit it.
Please make sure that all required properties are set before saving the object. We will recall them in the subsection about the individual element properties.Sample PropertiesA sample in Chemoition needs a molecule. Hence, you need to create a molecule first to save a new Sample.
To create a molecule you can use the molecule manager provided by the instance object.
So far two methodes are implemented to create a molecule object:# Create a molecule BH_3 by a smiles codem=instance.molecule().create_molecule_by_smiles('B')new_sample.molecule=m# Cr create the same molecule BH_3 by a InChI codem=instance.molecule().create_molecule_by_cls('BH3')new_sample.molecule=mnew_sample.save()The most common solvents are provided by Chemotion and can simply be created by thenew_solventfunction.# For instancesolv=col_1.new_solvent('CDCl3')solv.save()To print a list of all provided solvents call:print(instance.get_solvent_list())Another feature of Samples is the possibility of splitting.
Simply call thesplitfunction to create a split of the origin sample.new_spit_sample=new_sampel.split()new_spit_sample.save()Reaction PropertiesIn order to work with a Reaction you must determine the materials required for the reaction.
The keys the materials in the properties dict of a reaction object are:starting_materials,reactants,products,solventsandpurification_solvents.
These materials are represented as list object in the Reaction object.
You can only add Sample objects to it. By adding a sample to the reaction materials
a new split of the sample is automatically created and added to the reaction.solv=col_1.new_solvent('CDCl3')new_reaction.properties['starting_materials'].append(new_sampel)new_reaction.properties['purification_solvents'].append(solv)new_reaction.save()Wellplate PropertiesWellplates have a new properies called wells.
The singel wells of the wellplate can be addressed by a row number a column letter.
You can only add Samples to the wells.
By adding a sample to a well a new split of the sample is automatically created and added to the well.new_wellplate.wells[0]['A']=new_sample# from how to create samplesnew_wellplate.wells[0]['b']=solv# from how to create solventsnew_wellplate.wells[1]['b']=sample# from how to fetch samplesnew_wellplate.save()Research Plan Properties-> ToDo: Not jet implementedContributingContributions to ChemotionApi are welcome! If you have any bug reports, feature requests, or suggestions, please
open an issue on the GitHub repository. You can also submit
pull requests with your proposed changes.LicenseChemotionApi is licensed under the MIT License.Let me know if there's anything else I can help you with!