To improve the usability of the API, the GroLink project includes a Python library, that links Python code to the running API server.
There are two ways to access the GroPy library, install it via pip or add the source file directly. Installing it with pip is much more handy on the long turn and is therefore recommended, yet the functionality is the same.
A pip packed is included in the GroPy repository in the Package registry. This can be installed by the following pip command. Make sure pip is installed on your system.
pip install GroPy –index-url https://gitlab.com/api/v4/projects/50527255/packages/pypi/simple
This command works like all other pip commands, including –upgrade
for updating to a new version or pip uninstall GroPy
to remove the library.
Now GroPy can be imported to all your projects by from GroPy import GroPy
If for any reason the pip installation is not an option or not wanted it is also possible to just download the GroPy.py from the repository.
If this file is in the same directory as your Python file you can add it with import GroPy
.
It is at any point necessary that GroIMP is running as the API server. How to start it is explained here.
Now in an empty python file (.py) we first import GroPy and create a link to the API server.
from GroPy import GroPy #if you did not use pip change the line to import GroPy link = GroPy.GroLink("http://localhost:58081/api/") # if you started the API on a other port change it here as well
This link object by now did not interact with the server, it is only a collection of commands to create calls. These commands can be found here.
Each GroIMP project is always embedded in a workbench, which handles the interaction with the project. GroPy creates references to these workbenches (WBRef). For this tutorial a new empty RGG workbench is created, this is similar to the one created by 'new RGG' on the GUI.
wbCreateCall = link.createWB("newRGG") # create the API call object wbCreateCall.run() # execute the API call object wb1 = wbCreateCall.read() # reading the results of the API call and create a WBRef object
The code above shows how GroPy handles calls. A call is created by the functions of the GroLink object 'link'. This call represents an API request but did not interact with the API yet. This happens when the run()
function is executed. Afterwards, the response is stored in the call object and is interpreted when the read()
function is executed.
This can be simplified by:
wb1 = link.createWB("newRGG").run().read() # a inline solution of the code above
The now-created WBRef object wb1 can execute a list of commands to create new calls. These commands can be found here
With the WBRef object 'wb1' it is now possible to get information about the workbench, for this tutorial: the project graph and the available RGG functions.
#print the Porject graph print(wb1.getProjectGraph().run().read()) #get the list of RGG functions: functions = wb1.listRGGFunctions().run().read() for f in functions['data']: # reading all entries form the data entry print(f)
By default, each function that is defined with the return type 'JSON', returns a dictionary that always includes 'console' for the content of the XL console and 'log' for application information. Additionally, in most cases, the data that was requested is returned in 'data'. In some more complex results such as the ProjectGraph, the response is divided into several dictionary entries.
In the next step, the model will be manipulated by RGG functions and XL queries.
# create the call that counts all A nodes countA = wb1.runXLQuery("count((*Model.A*));") # create the call that executes the rgg command 'run' execRun=wb1.runRGGFunction("run") # executing the count and read the results print(countA.run().read()) # execturing the run function 10 times without printing because the result is not needed for i in range(10): execRun.run() # executing the count and read the results a second time print(countA.run().read())
XL queries as shown above work similarly to the XL console in the GUI(except for the usage of Variables), including the usage of rewriting rules. The main difference to the usage of XL in RGG is that Modules defined in the RGG code such as A
must be referred to with Model.A
.
The call created with runRGGFunction does the same thing as clicking on the button in the GUI.
At the final step of this small tutorial, the workbench is saved and closed.
If the workbench is not saved before closing the changes will be lost without additional questions.
The code below shows the two different ways to save a project:
# Saving the retuned data data=wb1.save().run().read() # recive the gsz file as binary data f = open("result.gsz",'wb') # open a binary file f.write(data) # save the file f.close() # close the file # save to a path on the System wb1.save(path="/home/tim/Dokumente/Uni/master/lab/py/test/eins.gs").run()
The two different ways exist for two different use cases: The first one does not require a shared file system between the API and the client. This means it is possible to run GroIMP on a different system such as a remote computer or a container(e.g. Docker). Therefore the first way also only supports .gsz files, since all information is returned as one file. The second way might be simpler in other use cases.
Finally closing the workbench is a simple last command:
# close the workbench wb1.close().run().read()
Afterward, the API can be closed by crl+c in the console or with the command link.close().run()
;
To simplify the testing and understanding here is the complete code of the tutorial:
from GroPy import GroPy #if you did not use pip change the line to import GroPy link = GroPy.GroLink("http://localhost:58081/api/") # if you started the API on a other port change it here as well wb1 = link.createWB("newRGG").run().read() #print the Porject graph print(wb1.getProjectGraph().run().read()) #get the list of RGG functions: functions = wb1.listRGGFunctions().run().read() for f in functions['data']: # reading all entries form the data entry print(f) # create the call that counts all A nodes countA = wb1.runXLQuery("count((*Model.A*));") # create the call that executes the rgg command 'run' execRun=wb1.runRGGFunction("run") # executing the count and read the results print(countA.run().read()) # execturing the run function 10 times without printing because the result is not needed for i in range(10): execRun.run() # executing the count and read the results a second time print(countA.run().read()) # Saving the retuned data data=wb1.save().run().read() # recive the gsz file as binary data f = open("result.gsz",'wb') # open a binary file f.write(data) # save the file f.close() # close the file #close wb1.close().run().read()