Joule Application Programming Interface (API)
Note
The Joule Application Programming Interface (API) uses asynchronous coroutines and must be run inside an event loop. To run the examples in this documentation interactively use the asyncio module:
A Jupyter Notebook that provides an overview of the API functionality is available from GitHub. To run this notebook follow the commands below to install Jupyter and matplotlib, retrieve the notebook file, and start the Jupyter server:
Joule Nodes
A Node represents a Joule instance and is the only means to access API methods. Do not create manually create a Node object. Instead, use joule.api.get_node to create a connection to a specific node. Joule modules have a node object created automatically that refers to the Joule node running it.
# Inside a joule.client.ReaderModule class
async def run(self, parsed_args, output):
node_info = await self.node.info()
# other code ...
The rest of this section describes class methods separated by category.
annotation_create(annotation, stream)
async
Add an annotation. Create a new :class:joule.api.Annotation object locally and associate it with a data stream.
The stream may be specified by path, ID, or a :class:joule.api.DataStream object
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
annotation
|
Annotation
|
the annotation to add |
required |
stream
|
DataStream | str | int
|
reference to the DataStream to annotate |
required |
Returns:
| Type | Description |
|---|---|
Annotation
|
Annotation with ID populated |
Example
>>> from joule import utilities
>>> event_note = Annotation(title='Event Annotation',
start=utilities.human_to_timestamp('now'))
>>> await node.annotation_create(event_note, '/path/to/stream')
>>> range_note = Annotation(title='Range Annotation',
start=utilities.human_to_timestamp('1 minute ago'),
end=utilities.human_to_timestamp('now'))
>>> await node.annotation_create(range_note, '/path/to/stream')
annotation_delete(annotation)
async
annotation_get(stream, start=None, end=None)
async
Retrieve annotations for a particular stream. Specify timestamps to only retrieve annotations over
a particular interval. The stream may be specified by path, ID, or a :class:joule.api.DataStream object
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to a DataStream |
required |
start
|
Optional[int]
|
UNIX timestamp (microseconds).. default to the beginning of the data |
None
|
end
|
Optional[int]
|
UNIX timestamp (microseconds). defaults to the end of the data |
None
|
Returns:
| Type | Description |
|---|---|
List[Annotation]
|
list of Annotations that match the specified criteria |
annotation_update(annotation)
async
data_consolidate(stream, max_gap, start=None, end=None)
async
Remove interval breaks less than max_gap microseconds in a data stream. Useful for merging data streams with unintentional gaps due to
importing data at different times. NOTE: This operation does not recompute the higher level decimations needed to display the data stream in Lumen.
If data does not appear in Lumen after consolidating a large number of small intervals, run :meth:Node.data_decimate
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to a DataStream |
required |
start
|
Optional[int]
|
UNIX timestamp (microseconds). Defaults to the beginning of the data. |
None
|
end
|
Optional[int]
|
UNIX timestamp (microseconds). Defaults to the end of the data. |
None
|
max_gap
|
int
|
merge intervals of data that are less than this many microseconds apart. |
required |
Returns:
| Type | Description |
|---|---|
List
|
the list of remaining intervals after consolidating |
data_decimate(stream)
async
Recompute the decimations for a data stream.
Note
This can take a long time for large streams**
data_delete(stream, start=None, end=None)
async
Delete data from a stream. Specify timestamp bounds to delete a particular range or omit to delete all data. Deleting a range of data creates an interval break in the stream as show in the example below.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to a DataStream |
required |
start
|
Optional[int]
|
UNIX timestamp (microseconds). Defaults to the beginning of the data. |
None
|
end
|
Optional[int]
|
UNIX timestamp (microseconds). Defaults to the end of the data. |
None
|
Example
>>> await node.data_intervals("/parent/my_folder/stream")
[[1551759387204004, 1551863787204004]]
>>> left=1551795387204004 # Tue, 05 Mar 2019 08:16:27
>>> right=1551831387204004 # Tue, 05 Mar 2019 19:16:27
>>> await node.data_delete("/parent/my_folder/stream", start=left, end=right)
>>> await node.data_intervals("/parent/my_folder/stream")
[[1551759387204004, 1551791787204004],
[1551831387204004, 1551863787204004]]
data_drop_decimations(stream)
async
Remove the decimated copies of this stream. This can be useful for reducing the size of the database if Lumen visualizations are not needed for the data stream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to a DataStream |
required |
data_intervals(stream, start=None, end=None)
async
Retrieve list of data intervals. See :ref:sec-intervals for details on data intervals. Specify timestamp bounds to
list intervals over a particular range or omit to list all intervals.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to a DataStream |
required |
start
|
Optional[int]
|
UNIX timestamp (microseconds). Defaults to the beginning of the data. |
None
|
end
|
Optional[int]
|
UNIX timestamp (microseconds). Defaults to the end of the data. |
None
|
Example
>>> await node.data_intervals("/parent/my_folder/stream") # unbroken data stream
[[1551759387204004, 1551863787204004]]
>>> await node.data_intervals("/parent/my_folder/stream") # one segment of missing data
[[1551730769556442, 1551757125630201],
[1551758221956119, 1551811071052711]]
>>> await node.data_intervals("/parent/my_folder/stream") # no stream data
[]
data_read(stream, start=None, end=None, max_rows=None)
async
Read historic data from a stream. Specify timestamp bounds for a particular range or omit to read all historic data.
This method returns a pipe which should be used to read the data. The pipe must be closed after use. See :ref:pipes
for details on Joule Pipes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to a DataStream |
required |
start
|
Optional[int]
|
Timestamp in UNIX microseconds. Omit to read from beginning of the stream. |
None
|
end
|
Optional[int]
|
Timestamp in UNIX microseconds. Omit to read until the end of the stream. |
None
|
max_rows
|
Optional[int]
|
Return a decimated view of the data with at most this many rows, decimations are provided in powers of 4. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
a |
Pipe
|
class: |
Example
>>> pipe = await node.data_read("/parent/my_folder/stream")
>>> data = await pipe.read() # for large data run in a loop
array([(1551730769556442, 0), (1551730769657062, 10),
(1551730769757882, 20), ..., (1551751850688250, 661380),
(1551751850795677, 661390), (1551751850896756, 661400)],
dtype=[('timestamp', '<i8'), ('data', '<i4')])
>>> pipe.consume(len(data)) # flush the pipe
>>> await pipe.close() # close the data connection
data_stream_annotation_delete(stream, start=None, end=None)
async
Remove annotations from this stream. If start and/or end is specified only remove annotations within this interval.
If either start or end are omitted the range defaults to the beginning or end of the stream respectively.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to the target DataStream |
required |
start
|
Optional[int]
|
only remove annotations after (>=) to UNIX timestamp |
None
|
end
|
Optional[int]
|
only remove annotation before (<=) to UNIX timestamp |
None
|
data_stream_create(stream, folder)
async
Create a stream and place in the specified folder. Folder may be specified by object, path or numeric ID.
See :class:joule.api.DataStream for details on creating DataStream objects. Raises :exc:joule.errors.ApiError if the
stream or folder specification is invalid. If the folder is specified by path it will be created if it does not exist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream
|
new DataStream object |
required |
folder
|
Folder | str | int
|
reference to desired parent Folder |
required |
Returns:
| Type | Description |
|---|---|
DataStream
|
newly created DataStream object with ID field populated |
Example
>>> new_stream = joule.api.DataStream(name="New Stream")
>>> new_stream.elements = [joule.api.Element(name="Element1")]
>>> await node.data_stream_create(new_stream,"/parent/new_folder")
<joule.api.DataStream id=2628 name='New Stream' description='' datatype='float32'
is_configured=False is_source=False is_destination=False locked=False decimate=True>
data_stream_delete(stream)
async
Delete a stream. DataStream may be specified by a :class:joule.api.DataStream object,
a path, or numeric ID. Raises :exc:joule.errors.ApiError if the stream specification
is invalid or if the stream is locked. To remove data within a stream see :meth:Node.data_delete.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to target DataStream |
required |
Example
>>> new_stream = joule.api.DataStream(name="New Stream")
>>> new_stream.elements = [joule.api.Element(name="Element1")]
>>> await node.data_stream_create(new_stream,"/parent/new_folder")
<joule.api.DataStream id=2628 name='New Stream' description='' datatype='float32'
is_configured=False is_source=False is_destination=False locked=False decimate=True>
data_stream_get(stream)
async
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to the target DataStream |
required |
Returns:
| Type | Description |
|---|---|
DataStream
|
requested DataStream object |
Example
>>> stream = await node.data_stream_get("/parent/my_folder/stream")
<joule.api.DataStream id=2627 name='stream' description='' datatype='int32'
is_configured=False is_source=False is_destination=False locked=False decimate=True>
>>> stream = await node.data_stream_get(2627)
<joule.api.DataStream id=2627 name='stream' description='' datatype='int32'
is_configured=False is_source=False is_destination=False locked=False decimate=True>
>>> await node.data_stream_get("/does/not/exist") # raises ApiError
joule.errors.ApiError: stream does not exist [404]
data_stream_info(stream)
async
Get information about a stream as a joule.api.DataStreamInfo object. DataStream may be specified
by a joule.api.DataStream object, a path, or numeric ID. Raises :exc:joule.errors.ApiError
if the stream specification is invalid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to target DataStream |
required |
Returns:
| Type | Description |
|---|---|
DataStreamInfo
|
information about the target DataStream |
data_stream_move(stream, folder)
async
Move a DataStream into a different Folder. The stream and folder may be
specified by objects, paths, or numeric ID's. The stream name must be unique in the destination
and not be locked (actively in use by a module or statically configured). Raises
:exc:joule.errors.ApiError if stream or folder specifications are invalid or the requested
move cannot be performed. The destination is automatically created if it does not exist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to target DataStream |
required |
folder
|
Folder | str | int
|
reference to desired parent Folder |
required |
Example
>>> await node.data_stream_move("/parent1/my_folder/stream","/parent2")
>>> parent2 = await node.folder_get("/parent2")
>>> parent2.data_streams
[<joule.api.DataStream id=2627 name='stream' description='' datatype='int32'
is_configured=False is_source=False is_destination=False locked=False decimate=True>]
>>> await node.data_stream_move("/does/not/exist","/parent2") # raises ApiError
joule.errors.ApiError: stream does not exist [404]
data_stream_update(stream)
async
Update stream and element attributes.
Raises :exc:joule.errors.ApiError if stream is locked or the specification is invalid.
The datatype and number of elements may not be changed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream
|
DataStream object with modified attributes |
required |
Example
>>> stream = await node.data_stream_get("/parent/my_folder/stream")
>>> stream.elements # Element name is "Element1"
[<joule.api.Element id=3192 index=0, name='Element1' units=None
plottable=True display_type='CONTINUOUS'>]
>>> stream.elements[0].name="New Name"
>>> await node.data_stream_update(stream) # send updated values to Joule
>>> updated_stream = await node.data_stream_get(stream) # refresh local copy
>>> updated_stream.elements # Element name is now "New Name"
[<joule.api.Element id=3192 index=0, name='New Name' units=None
plottable=True display_type='CONTINUOUS'>]
data_subscribe(stream)
async
Read live data from a stream. The stream must be actively produced by a module. This method returns a pipe which
should be used to read the data. The pipe must be closed after use. See :ref:pipes
for details on Joule Pipes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to a DataStream |
required |
Returns:
| Type | Description |
|---|---|
Pipe
|
a Pipe connection to the live DataStream |
Example
>>> pipe = await node.data_read("/live/stream")
>>> data = await pipe.read() # run in a loop for continuous updates
array([(1551730769556442, 0), (1551730769657062, 10),
(1551730769757882, 20), ..., (1551751850688250, 661380),
(1551751850795677, 661390), (1551751850896756, 661400)],
dtype=[('timestamp', '<i8'), ('data', '<i4')])
>>> pipe.consume(len(data)) # flush the pipe
>>> await pipe.close() # close the data connection
data_write(stream, start=None, end=None, merge_gap=0)
async
Write data to a stream. The stream must not be an active destination from any other source.
Optionally specify start and end timestamps to remove existing data over the interval you plan to write.
Omit to merge the new data with the existing data. This method returns a pipe which
should be used to write the data. The pipe must be closed after use. See :ref:pipes
for details on Joule Pipes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
DataStream | str | int
|
reference to a DataStream |
required |
start
|
Optional[int]
|
UNIX timestamp (microseconds). Omit to merge with existing data |
None
|
end
|
Optional[int]
|
UNIX timestamp (microseconds). Omit to merge with existing data |
None
|
merge_gap
|
int
|
merge with surrounding intervals if gap is less than this |
0
|
Example
>>> import numpy as np
>>> pipe_out = await node.data_write("/parent/my_folder/stream")
>>> for i in range(4):
... await pipe_out.write(np.array([[joule.utilities.time_now(), i]]))
>>> await pipe_out.close()
>>> pipe_in = await node.data_read("/parent/my_folder/stream") # read the data back
>>> await pipe_in.read()
array([(1551758297114942, 0.), (1551758297115062, 1.),
(1551758297115090, 2.), (1551758297115111, 3.)],
dtype=[('timestamp', '<i8'), ('data', '<f4')])
db_connect()
async
Create a connection to the node database. Note the node's pg_hba.conf
must allow remote connections to the database. The connection provides readonly
access to the data and metadata schemas but does allow creation of new schemas
or table creation in the public schema.
Returns:
| Type | Description |
|---|---|
Engine
|
authenticated connection to the database as the |
db_connection_info()
async
Connection information necessary to connect to the node database. This
is useful if the IP address or domain name must be changed before connecting
to the databse. Returns a :class:joule.utilities.ConnectionInfo object.
Returns:
| Type | Description |
|---|---|
ConnectionInfo
|
information necessary to establish a connection as the |
event_stream_count(stream, start=None, end=None, json_filter=None, include_on_going_events=True)
async
The parameters are the same as :meth:Node.event_stream_read_list.
Returns:
| Type | Description |
|---|---|
int
|
The number of Events matching the specified criteria |
event_stream_create(stream, folder)
async
Create an event stream and place in the specified folder. Folder may be specified by object, path or numeric ID.
See :class:joule.api.EventStream for details on creating EventStream objects. Raises :exc:joule.errors.ApiError if the
stream or folder specification is invalid. If the folder is specified by path it will be created if it does not exist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
EventStream
|
the EventStream to create |
required |
folder
|
Folder | str | int
|
reference to the desired parent Folder |
required |
Returns:
| Type | Description |
|---|---|
EventStream
|
configured EventStream with ID populated |
event_stream_delete(stream)
async
Delete a stream. EventStream may be specified by a :class:joule.api.EventStream object,
a path, or numeric ID. Raises :exc:joule.errors.ApiError if the stream specification
is invalid or if the stream is locked. To remove data within a stream see :meth:Node.event_stream_remove.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
EventStream | str | int
|
reference to an EventStream |
required |
Example
>>> folder = await node.folder_get("/parent/my_folder")
>>> folder.event_streams # my_folder has one stream
[<joule.api.EventStream id=39 name='stream' description='example'>
>>> await node.event_stream_delete("/parent/my_folder/stream") # delete the stream
>>> folder = await node.folder_get("/parent/my_folder")
>>> folder.event_streams # my_folder is now empty
[]
event_stream_get(stream, create=False, description='', chunk_duration='', chunk_duration_us=None, event_fields=None)
async
Retrieve the specified stream. EventStream may be specified by a :class:joule.api.EventStream object,
a path, or a numeric ID. Raises :exc:joule.errors.ApiError if stream specification is invalid. If stream is a
path and create=True then the EventStream is created if it does not exist using the description, chunk_duration specification
and event_fields parameters. If the stream exists these parameters are ignored. See :meth:Node.event_stream_update for modifying
existing streams. See :meth:Node.event_stream_create for more information creating streams.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
EventStream | str | int
|
reference to target stream |
required |
create
|
bool
|
create stream if it does not exist |
False
|
description
|
str
|
additional information about this stream |
''
|
chunk_duration
|
str
|
TimescaleDB chunk duration specified using time syntax (TODO) |
''
|
chunk_duration_us
|
Optional[int]
|
TimescaleDB chunk duration specified in microseconds |
None
|
event_fields
|
dictionary of event fields, see event field documentation (TODO) |
None
|
Returns:
| Type | Description |
|---|---|
EventStream
|
the newly created or pre-existing EventStream object |
event_stream_info(stream)
async
Get information about a stream as a :class:joule.api.EventStreamInfo object. EventStream may be specified
by a :class:joule.api.EventStream object, a path, or numeric ID. Raises :exc:joule.errors.ApiError
if the stream specification is invalid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
EventStream | str | int
|
reference to an EventStream |
required |
Returns:
| Type | Description |
|---|---|
EventStreamInfo
|
information about the EventStream |
event_stream_move(stream, folder)
async
Move a stream into a different folder. The stream and folder may be
specified by objects, paths, or numeric ID's. The stream name must be unique in the destination.
Raises :exc:joule.errors.ApiError if stream or folder specifications are invalid or the requested
move cannot be performed. The destination is automatically created if it does not exist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
EventStream | str | int
|
reference to EventStream |
required |
folder
|
Folder | str | int
|
reference to desired parent Folder |
required |
Example
>>> await node.event_stream_move("/parent1/my_folder/stream","/parent2")
>>> parent2 = await node.folder_get("/parent2")
>>> parent2.event_streams
[<joule.api.DataStream id=2627 name='stream' description='' datatype='int32'
is_configured=False is_source=False is_destination=False locked=False decimate=True>]
>>> await node.event_stream_move("/does/not/exist","/parent2") # raises ApiError
joule.errors.ApiError: stream does not exist [404]
event_stream_read(stream, start=None, end=None, json_filter=None, include_on_going_events=True, block_size=None)
Read events from an existing stream using an asynchronous generator. Specify block_size to
iterate over lists of events, otherwise iterates over each event individually.
Recommended for large streams where all events might not fit into memory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
EventStream | str | int
|
reference to an EventStream |
required |
start
|
Optional[int]
|
UNIX timestamp (microseconds). Omit to read from start of stream. |
None
|
end
|
Optional[int]
|
UNIX timestamp (microseconds). Omit to read to end of stream. |
None
|
limit
|
Limit query to first N result. |
required | |
json_filter
|
Optional[Dict[str, str]]
|
Select events based on content, see below. |
None
|
include_on_going_events
|
Set to |
True
|
|
block_size
|
Optional[int]
|
if > 1, yields a list of Events, otherwise yields a single Event |
None
|
Yields:
| Type | Description |
|---|---|
AsyncGenerator[Union[Event, List[Event]], None]
|
list of Events with max length = |
Example
Iterate over each event in a stream, or iterate over blocks of events (more efficient)
>>> async for event in node.event_stream_read(stream):
... print(f"processing {event}...")
>>> async for events in node.event_stream_read(stream, block_size=2):
... assert len(events)<=2 # maximum length is block_size
... print(f"processing {events}...")
event_stream_read_list(stream, start=None, end=None, json_filter=None, include_on_going_events=True, limit=10000)
async
Read events from an existing stream. Returns a list of :class:joule.api.Event objects. If the expected number
of events is very large consider using the async generator form of this function above.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
EventStream | str | int
|
reference to an EventStream |
required |
start
|
Optional[int]
|
UNIX timestamp (microseconds). Omit to read from start of stream. |
None
|
end
|
Optional[int]
|
UNIX timestamp (microseconds). Omit to read to end of stream. |
None
|
limit
|
Limit query to first N result. |
10000
|
|
json_filter
|
Optional[Dict[str, str]]
|
Select events based on content, see below. |
None
|
include_on_going_events
|
Set to |
True
|
Returns:
| Type | Description |
|---|---|
List[Event]
|
A list of Events matching the specified criteria |
JSON Filter Format
Specify a list of lists where each item is a tuple of [key,comparison,value]
Outer lists are OR conditions, inner lists are AND conditions. Comparison must
be one of the following
- String Comparison:
is, not, like, unlike - Numeric Comparison:
gt, gte, lt, lte, eq, neq
Example
Retrieve the first 100 events where `name` is LIKE `sample` OR `value` is between 0 AND 10:
>>> # ((-------- test ----------) OR (------test------ AND ------test-------))
>>> filter = [[['name','like','sample']] , [['value','gt',0], ['value','lt',10]]]
>>> await node.event_stream_read_list(stream,limit=100,json_filter=json.dumps(filter))
event_stream_remove(stream, start=None, end=None, json_filter=None)
async
Remove events from an existing stream. EventStream may be specified by a :class:joule.api.EventStream object,
a path, or numeric ID. Specify start and/or end timestamps to remove over events over a specific time range. Use
json_filter to remove events based on content.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
EventStream | str | int
|
reference to an EventStream |
required |
start
|
Optional[int]
|
UNIX timestamp (microseconds). Omit to read from start of stream. |
None
|
end
|
Optional[int]
|
UNIX timestamp (microseconds). Omit to read to end of stream. |
None
|
json_filter
|
Select events based on content, see below. |
None
|
event_stream_update(stream)
async
Update the event stream name and/or description. Raises :exc:joule.errors.ApiError if
the specification is invalid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
EventStream
|
EventStream object with updated attributes |
required |
Example
>>> stream = await node.event_stream_get("/parent/my_folder/stream")
>>> stream.elements # Element name is "Element1"
[<joule.api.Element id=3192 index=0, name='Element1' units=None
plottable=True display_type='CONTINUOUS'>]
>>> stream.elements[0].name="New Name"
>>> await node.data_stream_update(stream) # send updated values to Joule
>>> updated_stream = await node.data_stream_get(stream) # refresh local copy
>>> updated_stream.elements # Element name is now "New Name"
[<joule.api.Element id=3192 index=0, name='New Name' units=None
plottable=True display_type='CONTINUOUS'>]
event_stream_write(stream, events)
async
Add events to an existing stream. EventStream may be specified by a :class:joule.api.EventStream object,
a path, or numeric ID. Events is a list of :class:joule.api.Event objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
EventStream | str | int
|
reference to an EventStream |
required |
events
|
List[Event]
|
Event objects to add to the stream |
required |
Returns:
| Type | Description |
|---|---|
List[Event]
|
list of Event objects with ID populated |
Example
>>> from joule.utilities import time_now
>>> e1 = Event(start_time=time_now()-1e6, end_time = time_now(), content={'name': 'event1'})
>>> await asyncio.sleep(2)
>>> e2 = Event(start_time=time_now()-1e6, end_time = time_now(), content={'name': 'event2'})
>>> await node.event_stream_write("/plugs/events",[e1,e2])
folder_delete(folder, recursive=False)
async
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
folder
|
Folder | str | int
|
reference to target folder |
required |
recursive
|
bool
|
if |
False
|
folder_get(folder)
async
Retrieve the specified folder. Folder may be specified by a :class:joule.api.Folder object,
a path, or numeric ID. Raises :exc:joule.errors.ApiError if folder specification is invalid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
folder
|
Folder | str | int
|
reference to target Folder |
required |
Returns:
| Type | Description |
|---|---|
Folder
|
requested Folder object |
Example
>>> folder = await node.folder_get("/parent/my_folder") # query by path
<joule.api.Folder id=2729 name='archive' description=None locked=True>
>>> live = await node.folder_get(2730) # query by ID
<joule.api.Folder id=2730 name='my_folder' description=None locked=True>
>>> await node.folder_get("/does/not/exist") # raises ApiError
joule.errors.ApiError: folder does not exist [404]
folder_move(source, destination)
async
Move the source folder into the destination folder. The source and destination may be
specified by joule.api.Folder objects, paths, or numeric ID's. The source folder name must be unique in the destination
and not be locked (actively in use by a module or statically configured) Raises
:exc:joule.errors.ApiError if folder specifications are invalid or the requested
move cannot be performed. The destination is automatically created if it does not exist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Folder | str | int
|
reference to the target folder |
required |
destination
|
Folder | str | int
|
reference to the desired parent folder |
required |
Example
>>> await node.folder_move("/parent1/my_folder","/parent2")
>>> parent2 = await node.folder_get("/parent2")
>>> parent2.children
[<joule.api.Folder id=3321 name='my_folder' description=None locked=False>]
>>> await node.folder_move("/parent1/missing_folder","/parent2") # rasises ApiError
joule.errors.ApiError: folder does not exist [404]
folder_root()
async
Retrieve the node's root folder. This returns the entire database structure as shown in the example below.
Returns:
| Type | Description |
|---|---|
Folder
|
root folder object which includes the full folder hierarchy |
Example
>>> root = await node.folder_root()
>>> root
<joule.api.Folder id=2728 name='root' description=None locked=True>
>>> root.children
[<joule.api.Folder id=3123 name='tmp' description=None locked=False>,
<joule.api.Folder id=2729 name='archive' description=None locked=True>,
<joule.api.Folder id=2730 name='live' description=None locked=True>]
folder_update(folder)
async
Update folder attributes. The name and description are the only writable attributes.
Raises :exc:joule.errors.ApiError if folder is locked or the specification is invalid
Example
>>> folder = await node.folder_get("/parent/my_folder")
<joule.api.Folder id=3329 name='my_folder' description=None locked=False>
>>> folder.name="new name"
>>> await node.folder_update(folder) # save the change
>>> parent = await node.folder_get("/parent")
>>> parent.children # folder 3329 has a new name
[<joule.api.Folder id=3329 name='new name' description=None locked=False>]
module_get(module, statistics=False)
async
Retrieve a specific module as a :class:joule.api.Module object. If statistics is True
retrieve CPU and memory statistics for each module. Collecting statistics takes additional time
because the CPU usage is averaged over a short time interval. Module may be specified by object,
name, or numeric ID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module | str | int
|
reference to a Module |
required |
statistics
|
bool
|
include memory and CPU usage information |
False
|
Returns:
| Type | Description |
|---|---|
Module
|
the requested Module |
module_list(statistics=False)
async
Retrieve a list of the current Joule modules as :class:joule.api.Module objects. If statistics is True
retrieve CPU and memory statistics for each module. Collecting statistics takes additional time
because the CPU usage is averaged over a short time interval.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
statistics
|
bool
|
include memory and CPU usage information |
False
|
Returns:
| Type | Description |
|---|---|
List[Module]
|
list of the active modules |
module_logs(module)
async
Retrieve a list of module logs. Logs are the stdout and stderr streams from the module. The easiest way to generate
logs is by adding print statements to a module. The maximum number of lines is controlled by the MaxLogLines parameter
in the main configuration file (see :ref:sec-system-configuration). Logs are automatically rolled if a module produces
more than the maximum number of lines. The module may be specified by object, name, or numeric ID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module | str | int
|
reference to a Module |
required |
Returns:
| Type | Description |
|---|---|
List[str]
|
list of recently printed lines to stderr and stdout |
proxy_get(proxy)
async
Retrieve a specific proxy as a :class:joule.api.Proxy object. Proxy may be specified by object,
name, or numeric ID.
Returns:
| Type | Description |
|---|---|
Proxy
|
requested Proxy object |
proxy_list()
async
Retrieve a list of proxied URL's as :class:joule.api.Proxy objects.
Returns:
| Type | Description |
|---|---|
List[Proxy]
|
a list of proxied URL's |
Working with Nodes
joule.api.delete_node(node)
Remove a node from the current user's configuration
Warning
This operation will remove the key material for the node and cannot be reversed
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node
|
str | BaseNode
|
the node to remove |
required |
joule.api.get_node(name='')
Connect to a Joule node. Retrieve a list of
node names with CLI tool joule node list
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
omit to use the default node |
''
|
Returns:
| Type | Description |
|---|---|
BaseNode
|
a connection to the Joule node |
joule.api.get_nodes()
Returns:
| Type | Description |
|---|---|
List[BaseNode]
|
all nodes that the current user has access to |
joule.api.save_node(node)
Persist any changes to node attributes (name, URL, or key) Args: node: modified node to save
API Models
API Folder model. See :ref:sec-node-folder-actions for details on using the API to
manipulate folders. Folders are locked if they contain locked streams (streams that are active
or statically configured). Folder objects should not be created directly. They should be received
via API calls.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
folder name, must be unique in the parent |
''
|
description
|
str
|
optional field |
required |
locked
|
bool
|
folder may not be moved, deleted, or changed |
required |
data_streams
|
List[DataStream]
|
data streams in the folder |
required |
event_streams
|
List[EventStream]
|
event streams in the folder |
required |
children
|
List[Folder]
|
subfolders in the folder |
required |
API EventStream model. See :ref:sec-node-event-stream-actions for details on using the API to
manipulate event streams.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
stream name, must be unique in the parent |
''
|
description
|
str
|
optional field |
''
|
API EventStreamInfo model. Received from :meth:Node.event_stream_info and should not be created directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
int
|
timestamp in UNIX microseconds of the beginning of the first event |
required |
end
|
int
|
timestamp in UNIX microsseconds of the end of the last data event |
required |
event_count
|
int
|
number of events in the stream |
required |
bytes
|
int
|
approximate size of the data on disk |
0
|
total_time
|
int
|
event stream duration in microseconds (start-end) |
0
|
API DataStream model. See :ref:sec-node-data-stream-actions for details on using the API to
manipulate data streams. Streams are locked if they are active or statically configured. When
creating a stream manually, omit the ID and status attributes (is_configured, is_source, is_destination, active, and locked),
these are set by the Joule server.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
stream name, must be unique in the parent |
''
|
description
|
str
|
optional field |
''
|
datatype
|
str
|
element datatype |
'float32'
|
keep_us
|
int
|
store the last N microseconds of data (-1 to keep all and 0 to keep none) |
-1
|
is_configured
|
is the stream statically configured with a |
required | |
is_source
|
is the stream an active data source |
required | |
is_destination
|
is the stream an active data destination |
required | |
active
|
bool
|
is the stream a source or destination |
required |
locked
|
bool
|
is the stream active or configured |
required |
decimate
|
bool
|
is the stream data decimated for visualization |
required |
elements
|
List[Element]
|
list of the stream elements |
None
|
API DataStreamInfo model. Received from :meth:Node.data_stream_info and should not be created directly.
.. warning:: Rows and Bytes values are approximate
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start
|
int
|
timestamp in UNIX microseconds of the first data element |
required |
end
|
int
|
timestamp in UNIX microsseconds of the last data element |
required |
rows
|
int
|
approximate rows of data in the stream |
required |
bytes
|
int
|
approximate size of the data on disk |
0
|
total_time
|
int
|
data duration in microseconds (start-end) |
0
|
API Element model. Streams have one or more elements. See :ref:sec-data-streams for details on the stream data model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
int
|
unique numeric ID assigned by Joule |
required |
index
|
int
|
column position in the data array (0 = first element) |
required |
name
|
str
|
element name |
''
|
units
|
str
|
unit of measurement, may be any string |
''
|
plottable
|
bool
|
should the element be visible in the Lumen plotting interface |
True
|
display_type [continous|discrete|event]
|
plot type, defaults to continuous |
required | |
offset
|
float
|
offset data visualization by |
required |
scale_factor
|
float
|
scale data visualation with above equation |
required |
default_max
|
float
|
fix auto scale max (set to None to fit plotted data) |
required |
default_min
|
float
|
fix auto scale min (set to None to fit plotted data) |
required |
API Module model. See :ref:sec-node-module-actions for details on using the API to
query modules. See :ref:modules for details on writing new modules. See :ref:sec-modules
for details on adding modules to Joule. Use :meth:Node.data_stream_get to retrieve the associated stream from the path string
in the inputs and outputs dictionaries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
int
|
unique numeric ID assigned by Joule server |
required |
name
|
str
|
module name, must be unique |
required |
description
|
str
|
optional field |
required |
is_app
|
bool
|
whether the module provides a web interface |
required |
inputs
|
Dict
|
Mapping of |
required |
outputs
|
Dict
|
Mapping of |
required |
statistics
|
ModuleStatistics
|
execution statistics, may be |
None
|
API ModuleStatistics model. Received by settings statistics to True when calling :meth:Node.module_get or
:meth:Node.module_info and should not be created directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pid
|
int
|
process ID |
required |
create_time
|
int
|
process creation time in UNIX seconds |
required |
cpu_percent
|
float
|
approximate CPU usage |
required |
memory_percent
|
float
|
approximate memory usage |
required |
API Annotation model. See :ref:sec-node-annotation-actions for details on using the API to
manipulate annotations. Annotations are associated with streams and may either coverage a range
of data or a single event. If end is None the annotation marks an event, otherwise
it marks a range.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
title
|
string
|
annotation title |
required |
content
|
string
|
additional description (optional) |
''
|
start
|
int
|
Unix microsecond timestamp |
required |
end
|
int
|
specify for range annotation, omit for event annotation |
None
|