Developing a app using SOAP¶
Developing an app involves connecting to Hydra through its SOAP or JSON API. Here we provide an example of simple app, which retrieves a network. This example uses the SUDS python library to connect. SUDS is a very easy to use library as it allows simple connection to a WSDL. For larger datasets, this may not be the library to use, however.
Creating a client¶
First, the WSDL must be identified and connected to the library.
By default, the hydra server is hosted on port 8080: http://localhost:8080
url = "http://localhost:8080/soap?wsdl"
cli = Client(url)
Using the cli.service
attribute, server functions can be called. The first one we must
use is login
.
Connect to the server¶
As the app connects to a remote server, a login is required so that data is protected.
For local use with one user, this can simply be read from a config file.
Once login is performed, the session_id
must be stored and added to the request
header for all subsequent requests
login_response = cli.service.login('myuser', 'Pa55w0rD')
token = cli.factory.create('RequestHeader')
token.session_id = login_response.session_id
#Now set the request header using cli.set_options:
cli.set_options(soapheaders=token)
#Finally, for easier usage, make a sensible namespace:
cli.add_prefix('hyd', 'soap_server.hydra_complexmodels')
Creating a network instance¶
Having successfully logged in, networks can be added, accessed and manipulated.
Hydra employs following structure:
Project ->
Networks ->
Nodes ->
Node Attributes
Links ->
Link Attributes
Scenarios ->
Data
This way, a project can contain multiple networks, which can in turn contain multiple scenarios. A scenario represents one ‘state’ of the network.
Before a network can be created, we must first create a project
proj = self.client.factory.create('hyd:Project')
proj.name = 'SOAP test %s'%(datetime.datetime.now())
project = self.client.service.add_project(proj)
A network can now be created
net = self.client.factory.create('hyd:Network')
net.name = args.network_name
net.description = "A network created by the example plugin"
net.project_id = project.id #Note that the project now has an ID, after adding it
Nodes can now be added to the project
nodes = self.client.factory.create('hyd:NodeArray')
node1 = self.client.factory.create('hyd:Node')
node1.id = -1
node1.name = "Node 1",
node1.description = "A node representing a water resource",
node1.x = 10
node1.y = 10
nodes.Node.append(node1)
node2 = self.client.factory.create('hyd:Node')
node1.id = -2
node2.name = "Node 2",
node2.description = "A node representing another water resource",
node2.x = 20
node2.y = 20
nodes.Node.append(node2)
...and now we link the nodes
links = self.client.factory.create('hyd:LinkArray')
node1 = self.client.factory.create('hyd:Link')
node1.name = "Link 1",
node1.description = "A link between two water resources",
node1.node_1_id = -1
node1.node_2_id = -2
nodes.Node.append(node1)
network.nodes = nodes
One slight complication with linking nodes is that the nodes do not yet have IDS. So how do the links what they are connecting? For this, temporary negative IDS are used. Notice on the nodes above, they have been assigned negative IDS. These will be replaced by permenant, positive IDS once the data is inserted into hydra. Negative IDs are only necessary if the object needs to be referred to and the referrer is not a direct descendant of the referee.
Now the network can be created
network = cli.service.add_network(net)
Attributes¶
Hydra provides the feature to assign attributes to nodes and links. For example, data associated with a node representing a water treatment plant might be ‘capacity’, ‘annual energy cost’ or ‘daily throughput’.
To achieve this, first the attributes themselves must be defined. Once an attribute is defined, it does not need to be defined again. It can be used throughout Hydra. A Name and Dimension uniquely define an attribute
#Define the attribute details
name = "Capacity"
dimension = Volume
#Check the attribute does not already exist.
attr = self.client.service.get_attribute(name, dimension)
if attr is None:
attr = cli.factory.create('hyd:Attr')
attr.name = name
attr.dimen = dimension
attr = self.client.service.add_attribute(attr)
Once the attribute has been defined, it can be assigned to the node. Going back to the network creation example, a node is defined as follows
node2 = self.client.factory.create('hyd:Node')
node1.id = -2
node2.name = "Node 2",
node2.description = "A node representing another water resource",
node2.x = 20
node2.y = 20
An attribute is added to this node using a ResourceAttr
object.
A ResourceAttr
links a resource (a network, node or link) to a network. Each has
its own id and ref_key, which indicates whether it refers to a node, link or network.
In this example, the node Node 2
is being given attribute Capacity
res_attr = cli.factory.create('hyd:ResourceAttr')
res_attr.ref_key = 'NODE'
res_attr.attr_id = attr.id
res_attr.id = -1
node.attributes.ResourceAttr.append(res_attr)
Note that a temporary negative ID is once again given to the ResourceAttr. This bears no relation to the negative ID on the node. It will be used later to associate data with this attribute. When the network is saved, this ID will be replaced by a permenant, positive, ID.
Scenarios and Data¶
Node and link attributes are not particularly useful by themselves without them
having a value. Using scenarios, attributes can have multiple values for different
purposes. For example, a network represenging a river network might have two
scenarios: Dry Year
and Wet Year
. While the topology of the network will
not change, the attributes of many of the nodes might change. Daily Throughput
of
our water treatment work will be less in a dry year compared to a wet year, for example.
In order to assign data to specific attributes, a scenario is used
scenario = self.client.factory.create('hyd:Scenario')
scenario.name = 'Dry Year'
scenario.description = 'Projected scenario of network in a dry year.'
Now data can be added
rs = cli.factory.create('hyd:ResourceScenario')
rs.resource_attr_id = -1 #This refers to the ID given to the resource attr earlier.
dataset = cli.factory.create('hyd:Dataset')
dataset.type = 'descriptor'
dataset.name = 'Volume of water in a reservoir during a dry year'
dataset.unit = 'ml'
dataset.dimension = 'Volume' # THis must match the dimension of the attribute.
dataset.hidden = 'N'
dataset.value = {'desc_val':100000}
scenario.resourcescenarios.ResourceScenario.append(dataset)
net.scenarios.Scenario.append(scenario)
#add the network...