Ranges
Categorize your data with ranges.
Ranges identify specific periods of time with a name. We can use the Python client to read from, write to, and attach metadata to these ranges.
Creating Ranges
To create a range, we can use the client.ranges.create
method:
import synnax as sy
from datetime import datetime
time_format = "%Y-%m-%d %H:%M:%S"
my_range = client.ranges.create(
# This name does not need to be unique, but it's a good idea to
# pick something that will be easy to identify later.
name="My Range",
time_range=sy.TimeRange(
start = datetime.strptime("2023-2-12 12:30:00", time_format),
end = datetime.strptime("2023-2-12 14:30:00", time_format),
),
)
Synnax will automatically generate a unique identifier for the range.
Only Create a Range if it Doesn’t Exist
If we only want to create a range if one with the same name doesn’t already exist, we
can pass in the retrieve_if_name_exists
parameter:
my_range = client.ranges.create(
name="My Range",
time_range=sy.TimeRange(
start = datetime.strptime("2023-2-12 12:30:00", time_format),
end = datetime.strptime("2023-2-12 14:30:00", time_format),
),
retrieve_if_name_exists=True,
)
In the event the range already exists, Synnax will return the existing range instead of creating a new one.
Retrieving Ranges
We can fetch a range using the client.ranges.retrieve
method.
Retrieving a Single Range
We can retrieve a range by its name or key:
# By name
my_range = client.ranges.retrieve("My Range")
# By key
my_range = client.ranges.retrieve(my_range.key)
Synnax will raise a NotFoundError
if the range does not exist, and a
MultipleFoundError
if multiple ranges with the given name exist. If you’d like to
accept multiple or no results, provide a list to the retrieve
method as shown below.
Retrieving Multiple Ranges
We can retrieve multiple ranges by passing a list of names or keys to the retrieve
method:
# By name
my_ranges = client.ranges.retrieve(["My Range", "My Other Range"])
# By key
my_ranges = client.ranges.retrieve([my_range.key, my_other_range.key])
# This won't work!
my_ranges = client.ranges.retrieve(["My Range", my_other_range.key])
In these examples, Synnax will not raise an error if a range cannot be found. Instead, the missing range will be omitted from the returned list.
Working with Channels
Accessing Channels
We can access the channels on a range as if they were class properties or dictionary keys:
my_range = client.ranges.retrieve("My Range")
# Using a property accessor
my_pressure_channel = my_range.pressure_2
# Using a dictionary accessor
my_pressure_channel = my_range["pressure_2"]
Accessing Multiple Channels
We can also access multiple channels on the range by passing a regular expression to our property accessor:
my_range = client.ranges.retrieve("My Range")
# Returns an iterable object containing matching channels
my_pressure_channels = my_range["^pressure"]
If we try to access channel-specific methods on the returned object, such as a name
or
data
, Synnax will raise MultipleFoundError
. Instead, we should iterate over the
returned list. Here’s a simple example where we plot the data from all of our pressure
channels:
import matplotlib.pyplot as plt
for ch in my_range["^pressure"]:
plt.plot(my_range.timestamps, ch, label=ch.name)
This iteration pattern is valid even if we only have one channel that matches our regular expression.
Aliasing Channels
Channels must maintain their original names, but situations arise where we’d like to give a channel a more descriptive name in the context of a particular range. Ranges allow us to do just that.
Imagine we have a channel named daq_analog_input_1
that we’d like to refer to as
tank_pressure
for a tank burst test. We can do this by aliasing the channel:
burst_test = client.ranges.retrieve("Oct 10 Burst Test")
# Set our alias
burst_test.daq_analog_input_1.set_alias("tank_pressure")
# We can also set an alias like this
burst_test.set_alias("daq_analog_input_1", "tank_pressure")
We can now access the channel using its alias:
burst_test.tank_pressure
Subsequent calls to set_alias
will overwrite the previous alias.
Aliases are only valid within the context of a particular range. If you try to access an aliased channel outside of the range, Synnax will not be able to find it.
Attaching Metadata
Setting Metadata
It’s common to have non-data information we’d like to attach to a particular range, such
as test configuration parameters, numeric results, part numbers, etc. We can attach this
metadata to a range using the meta_data
property:
burst_test = client.ranges.retrieve("Oct 10 Burst Test")
# Set a single key/value pair
burst_test.meta_data.set("part_number", "12345")
# Another way to set a single key/value pair
burst_test.meta_data["part_number"] = "12345"
# Set multiple key/value pairs
burst_test.meta_data.set({
"part_number": "12345",
"test_configuration": "Test 1",
"test_result": "123.45",
})
All metadata values are stored as strings. It’s up to you to correctly cast the values to the appropriate type.
Getting Metadata
Getting metadata is as easy as setting it:
burst_test = client.ranges.retrieve("Oct 10 Burst Test")
# Retrieve a single key
part_number = burst_test.meta_data.get("part_number")
# Another way to retrieve a single key
part_number = burst_test.meta_data["part_number"]
Deleting Metadata
We can delete metadata using the delete
method:
burst_test = client.ranges.retrieve("Oct 10 Burst Test")
# Delete a single key
burst_test.meta_data.delete("part_number")
# Another way to delete a single key
del burst_test.meta_data["part_number"]
# Delete multiple keys
burst_test.meta_data.delete(["part_number", "test_configuration"])
Deleting Ranges
Deleting a range is as simple as passing in its name or key to the delete
method:
client.ranges.delete("My Range")
client.ranges.delete(my_range.key)
Deleting a range by name will delete all ranges with that name. Be careful!
We can delete multiple ranges by passing a list of names or keys to the delete
method:
client.ranges.delete(["My Range", "My Other Range"])
client.ranges.delete([my_range.key, my_other_range.key])