Developing a app using JSON

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 requests python library to connect to JSON api.

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/json

from HydraLib.PluginLib import JsonConnection
url = "http://localhost:8080/soap?wsdl"
conn = JsonConnection(url)

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. Hydra provides a library which handles this through the JsonConnection object. To log in, just call login like so:

login_response = conn.login('myuser', 'Pa55w0rD')

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 = dict(name = 'SOAP test %s'%(datetime.datetime.now()))
project   = conn.call('add_project', {'project':proj})

A network can now be created .. code-block:: python

net = dict(
name = args.network_name description = “A network created by the example plugin” project_id = project.id #Note that the project now has an ID, after adding it

)

Nodes can now be added to the project

node1 = dict(
    id = -1
    name = "Node 1",
    description = "A node representing a water resource",
    x = 10,
    y = 10,

node2 = dict(
    node1.id = -2,
    node2.name = "Node 2",
    node2.description = "A node representing another water resource",
    node2.x = 20,
    node2.y = 20,
)

nodes = [node1, node2]

...and now we link the nodes

link = dict(
    name = "Link 1",
    description = "A link between two water resources",
    node_1_id = -1
    node_2_id = -2)
links = [link]

network['nodes'] = nodes
network['links'] = linkd

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 = conn.call('add_network', {'net':network})

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 = conn.call('get_attribute', ({'name':name, 'dimension':dimension})
if attr is None:
    attr = dict(
        name  = name,
        dimen = dimension)
    attr = conn.call('get_attribute', ({'attr':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 = dict(
    id = -2,
    name = "Node 2",
    description = "A node representing another water resource",
    x = 20,
    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 = dict(
    ref_key = 'NODE'
    attr_id = attr.id
    id      = -1
)
node.attributes. = [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 = dict(
    name        = 'Dry Year',
    description = 'Projected scenario of network in a dry year.',
    resourcescenarios = []
)

Now data can be added

rs = dict(
    resource_attr_id = -1 #This refers to the ID given to the resource attr earlier.
)

dataset = dict(
    type = 'descriptor',
    name = 'Volume of water in a reservoir during a dry year',
    unit = 'ml',
    dimension = 'Volume', # THis must match the dimension of the attribute.
    hidden = 'N',
    value = {'desc_val':100000},
)
rs['value'] = dataset

scenario['resourcescenarios'] = [rs]

net['scenarios'] = [scenario]
#add the network...