Objects
Contents
Objects¶
Heedy Objects represent Heedy’s core functionality. By default, Heedy supports
objects of the timeseries
type, but other types can be implemented by Heedy plugins, and can have
corresponding Python APIs registered with the Python client.
This page will describe how to use objects from the Python API, accessing them either from an app or a plugin.
Listing & Accessing¶
Objects can be accessed using the objects
property in Apps, Plugins, or Users:
app = heedy.App("your_access_token","http://localhost:1324")
objs = app.objects() # list objects that belong to the app
print(objs)
app = heedy.App("your_access_token","http://localhost:1324")
myuser = app.owner # Gets the user that owns this app
objs = myuser.objects() # list all permitted objects belonging to the user
print(objs)
p = heedy.Plugin(session="sync")
objs = p.objects() # access objects of any user on the server
print(objs)
app = heedy.App("your_access_token","http://localhost:1324",session="async")
objs = await app.objects() # list all objects that belong to the app
print(objs)
app = heedy.App("your_access_token","http://localhost:1324",session="async")
myuser = await app.owner # Gets the user that owns this app
objs = await myuser.objects() # list all objects that belong to the user
print(objs)
p = heedy.Plugin()
objs = await p.objects() # list all objects on the server
print(objs)
The list returned from the objects
function can be constrained, usually by object type, tags, or app key.
The tags are space-separated, and will match all objects with their superset.
app.objects(tags="steps fitbit",type="timeseries")
await app.objects(tags="steps fitbit",type="timeseries")
[Timeseries{'access': 'read',
'app': 'acf85b01-3c87-4813-9538-3c1120ecb2a3',
'created_date': '2021-03-12',
'description': '',
'id': 'e1f4d4c2-d4f0-431a-bcaf-58c512fc7564',
'key': 'steps',
'meta': {'schema': {'type': 'number'}},
'modified_date': '2021-09-27',
'name': 'Steps',
'owner': 'test',
'owner_scope': 'read',
'tags': 'fitbit steps charge5',
'type': 'timeseries'}]
Creating & Deleting¶
One can create new objects of a given type by calling the create
method in objects
:
obj = app.objects.create("My Timeseries",
type="timeseries",
meta={"schema":{"type":"number"}},
tags="myts mydata",
key="myts")
obj = await app.objects.create("My Timeseries",
type="timeseries",
meta={"schema":{"type":"number"}},
tags="myts mydata",
key="myts")
Only the first argument, the object name, is required. If not explicitly specified, the object type will be timeseries
, and all other fields will be empty.
When creating an object for an app, it is useful to give it a key
.
Keys are unique per-app, meaning that the app can have only one object with the given key. The object then can be retrieved using:
obj = app.objects(key="myts")[0]
obj = (await app.objects(key="myts"))[0]
Finally, to delete an object and all of its data, one can use the delete method:
obj.delete()
await obj.delete()
Note
Plugins have administrative access to the database, but to create an object belonging to the app when logged in using an App token, the app must have self.objects:create
scope. Similarly, deleting an object requires the self.objects:delete
scope. Finally, editing the object’s properties requires the self.objects:update
scope. Writing an object’s content/data requires the self.objects:write
scope. To give an app full access to manage its own objects, you can give it self.objects
super-scope.
Reading & Updating Properties¶
All properties available on the object can be accessed directly as properties in Python in two ways:
# Reads the key property from the server
assert obj.key == "myts"
# Uses the previously read cached data, avoiding a server query
assert obj["key"] == "myts"
# Reads the key property from the server
assert (await obj.key) == "myts"
# Uses the previously read cached data, avoiding a server query
assert obj["key"] == "myts"
When trying to access cached properties for an object that was not yet read from the server, obj["key"]
will return a KeyError
. To refresh the cache, it is sufficient to run:
obj.read()
assert obj["key"]=="myts"
await obj.read()
assert obj["key"]=="myts"
The read
function returns a dict containing the props. Any read or update for the object will also refresh the cache with new data.
To update properties, the update method can be used, or the props can directly be set if in a sync
session:
# Both of these lines do the same thing.
obj.description = "My description"
obj.update(description="My description")
#
#
await obj.update(description="My description")
Updating Metadata¶
Each object has a meta
property, which is defined separately for every object type. For example, the timeseries
object type defines meta
to contain a JSON schema describing the data type of each datapoint in the series:
[Timeseries{...
'meta': {'schema': {'type': 'number'}},
...
'type': 'timeseries'}]
Since the meta
property is a dict that can contain multiple sub-properties,
it has special handling in the API. For example, updating an object to set its meta property will merge the new values with the old ones:
obj.meta() # Read meta from server ={"a": 1,"b":2,"c":3}
obj.meta = {"c": 4,"b": None} # Update the b,c values
print(obj.meta) # {"a": 1,"c":4} # The update results are cached
await obj.meta() # Read meta from server ={"a": 1,"b":2,"c":3}
await obj.update(meta={"c": 4,"b": None}) # Update the b,c values
print(obj.meta) # {"a": 1,"c":4} # The update results are cached
Setting a key in meta
to None
resets it to its default value, so deleting a timeseries schema will simply reset the schema to {}
rather than removing the key.
Finally, for simplicity, the Python API supports direct modification of the meta object:
obj.meta.schema = {"type":"string"}
del obj.meta.b
await obj.meta.update(schema={"type":"string"})
await obj.meta.delete("b")
Timeseries Data¶
The timeseries
object type is built into heedy by default. A timeseries can be considered an array of datapoints of the form:
{
"t": 1234556789.0, // unix timestamp in seconds,
"d": 2, // data associated with the timestamp,
"dt": 60, //optional duration of the datapoint in seconds, [t,t+dt)
}
One can optionally specify a JSON schema as the schema
property of object metadata, which then constrains the data portion of future datapoints to the schema (see example in the metadata section above).
Reading Data¶
Suppose we have an object of type timeseries
.
Just like in Python arrays, heedy timeseries can be accessed by index, with negative numbers allowing indexing from the end of the series. One can also directly query data ranges and series length:
len(obj) # Number of datapoints in the series
obj[2:5] # Query by index range
obj[-1] # Get most recent datapoint
await obj.length() # Number of datapoints in the series
await obj(i1=2,i2=5) # Query by index range
await obj(i=-1)[0] # Get most recent datapoint
The timeseries data can also be accessed by timestamp and time range. Time ranges can be of two types: relative and absolute. A relative time range uses a string that specifies a timestamp relative to the current time (s,m,h,d,w,mo,y):
obj(t1="now-1w") # Returns the past week of data
await obj(t1="now-1w") # Returns the past week of data
The timeseries can also directly be accessed using the unix timestamp (in seconds) as the time range specifier:
# Returns the data from 2 hours ago to 1 hour ago
obj(t1=time.time()-2*60*60,t2=time.time()-60*60)
# Returns the data from 2 hours ago to 1 hour ago
await obj(t1=time.time()-2*60*60,t2=time.time()-60*60)
PipeScript Transforms¶
When querying data from a timeseries, you can also specify a server-side transform of the data, allowing aggregation and direct processing:
# Returns the sum of data for the past week
obj(t1="now-1w",transform="sum")
# Returns the sum of data for the past week
await obj(t1="now-1w",transform="sum")
Output DatapointArray¶
When reading timeseries data, by default the result is returned as a subclass of list
with a couple useful add-ons:
data = obj(t1="now-1w")
# Returns an array of just data portions of the result
data.d()
# Returns a pandas DataFrame of the timeseries data
data.to_df()
# Writes the data to a file
data.write("myfile.json")
data = await obj(t1="now-1w")
# Returns an array of just data portions of the result
data.d()
# Returns a pandas DataFrame of the timeseries data
data.to_df()
# Writes the data to a file
data.save("myfile.json")
Timeseries objects in Python can be configured to return pandas.DataFrames
directly instead (as is done in the heedy notebook plugin), or per query:
from heedy import Timeseries
Timeseries.output_type="dataframe" # Return pandas.DataFrames by default
obj(i1=-10,output_type="list") # override the global configuration for this query
from heedy import Timeseries
Timeseries.output_type="dataframe" # Return pandas.DataFrame by default
await obj(i1=-10,output_type="list") # override the global configuration for this query
Writing Data¶
If we have write access (write
scope), we can append a new datapoint to the timeseries directly, or write an array of data at once:
# Add a datapoint 5 with current timestamp to the series
obj.append(5)
assert obj[-1]["d"]==5
# Insert the given array of data
obj.insert_array([{"d": 6, "t": time.time()},{"d": 7, "t": time.time(), "dt": 5.3}])
# Add a datapoint 5 with current timestamp to the series
await obj.append(5)
assert (await obj(i=-1))["d"]==5
# Insert the given array of data
await obj.insert_array([{"d": 6, "t": time.time()},{"d": 7, "t": time.time(), "dt": 5.3}])
Removing Data¶
Removing data from a timeseries has identical semantics to querying data. In other words, it is sufficient to specify the range:
# Remove the last month of data
obj.remove(t1="now-1mo")
# Timeseries are indexed by timestamp, so a specific datapoint can be removed
# by calling remove with its timestamp
dp = obj[-1]
obj.remove(t=dp["t"])
# This is equivalent to the above:
obj.remove(i=-1)
# Remove the last month of data
await obj.remove(t1="now-1mo")
# Timeseries are indexed by timestamp, so a specific datapoint can be removed
# by calling remove with its timestamp
dp = await obj[-1]
await obj.remove(t=dp["t"])
# This is equivalent to the above:
await obj.remove(i=-1)
API¶
Objects¶
- class heedy.Objects(constraints, session)[source]¶
Bases:
heedy.base.APIList
Objects is a class implementing a list of objects. It is accessed as a property of users/apps/plugin to get the objects belonging to that user/app, or to query objects in all of heedy using plugin.
myuser.objects() # objects belonging to myuser myapp.objects() # objects managed by myapp plugin.objects() # all objects in heedy
await myuser.objects() # objects belonging to myuser await myapp.objects() # objects managed by myapp await plugin.objects() # all objects in heedy
- __call__(**kwargs)[source]¶
Gets the objects matching the given constraints. If used as a property of a user of an app, it will return only the objects belonging to that user/app (the user/app constraint is automatically added).
obj = p.objects(type="timeseries", key="mykey", tags="tag1 tag2", owner="myuser", app="")
obj = await p.objects(type="timeseries", key="mykey", tags="tag1 tag2", owner="myuser", app="")
- Parameters
type (str) – The type of the objects (like “timeseries”)
key (str) – The unique app key of the object (each app can only have one object with a given key)
tags (str) – The tags that the object must have, separated by spaces
owner (str) – The owner username of the object (set automatically when accessed using the objects property of a user)
app (str) – The app ID that the object belongs to. Set to empty string for objects that don’t belong to any app. (set automatically when accessing the objects property of an app)
icon (bool,False) – Whether to include the icons of the returned objects’ data.
- Returns
A list of
Object
matching the given constraints.
- Throws:
HeedyException: If the request fails.
- __getitem__(objectId)[source]¶
Gets an object by its ID. Each object in heedy has a unique string ID, which can then be used to access the object. The ID can be seen in the URL of the object’s page in the frontend.
obj = p.objects["d233rk43o6kkle43kl"]
obj = await p.objects["d233rk43o6kkle43kl"]
- Returns
The
Object
with the given ID (or promise for the object)
- Throws:
HeedyException: If the object does not exist
- create(name, meta={}, type='timeseries', **kwargs)[source]¶
Creates a new object of the given type (timeseries by default). Only the first argument, the object name is required.
obj = app.objects.create("My Timeseries", description="This is my timeseries", icon="fas fa-chart-line", type="timeseries", meta={"schema":{"type":"number"}}, tags="myts mydata" key="myts")
obj = await app.objects.create("My Timeseries", description="This is my timeseries", icon="fas fa-chart-line", type="timeseries", meta={"schema":{"type":"number"}}, tags="myts mydata" key="myts")
When creating an object for an app, it is useful to give it a key. Keys are unique per-app, meaning that the app can have only one object with the given key.
- Parameters
name (str,"") – The name of the object
description (str,"") – A description of the object
icon (str,"") – The icon of the object, either a base64 urlencoded image or fontawesome/material icon id.
owner (str) – The owner of the object (set automatically when accessed using the objects property of a user or app).
app (str) – The app ID that the object belongs to (set automatically when accessing the objects property of an app).
meta (dict,{}) – The metadata of the object.
type (str,"timeseries") – The type of the object
key (str) – The unique per-app key of the object
tags (str,"") – The tags assigned to the object, separated by spaces
owner_scope (str,"*") – The space-separated scopes to give the owner of the object if it is managed by an app.
- Returns
The newly created
Object
, with its data cached.
- Throws:
HeedyException: if the object could not be created.
Object¶
- class heedy.Object(cached_data, session)[source]¶
Bases:
heedy.base.APIObject
Object is the base class for all Heedy objects. For example, the Timeseries object type is a subclass of Object, and therefore includes all of the functionality described here.
When an object of an unrecognized type is returned from the Heedy API, the Python client will return it as the Object type.
- props = {'access', 'description', 'icon', 'key', 'meta', 'name', 'owner_scope', 'tags'}¶
Each element has the above properties available as attributes. In synchronous sessions, they allow you to update the properties directly:
o.name = "My new name" assert o.name == "My new name"
The above is equivalent to:
o.update(name="My new name") assert o["name"] == "My new name"
Note that each time you access the properties, they are fetched from the server. In non-interactive scripts it is useful to avoid redundant querying, so each read of data is cached. To use this cached data, you can access the property as a key. Instead of
o.name
, useo["name"]
, and callo.read()
to create/update the cache. Accessingo["name"]
will only work after the data was initially read, otherwise it will lead to aKeyError
.
- property app¶
The app that manages this object, if any:
if obj.app is not None: # Query the server for the object's app print(obj["app"].id) # Use cached value from previous query
if await obj.app is not None: # Query the server for the object's app print(obj["app"].id) # Use cached value from previous query
When accessing the Heedy API as an app, you will not be able to see or access any other apps.
- Returns
The app that this object belongs to, or None if it does not belong to an app. The returned
App
object does not have any cached data other than its id, so you will need to callread()
on it if accessing more than its id.
- delete(**kwargs)¶
Calls the element’s URI with the DELETE method. This is a method available for all subclasses of APIObject (objects, apps, users, etc), and removes all associated data from Heedy.
o.delete() o.read() # Throws error - it longer exists!
await o.delete() await o.read() # Throws error - it no longer exists!
- Parameters
**kwargs – Arguments to pass as query parameters to the server (usually empty)
- Raises
HeedyException – If the server returns an error, or when the app does not have permission to delete.
- property id¶
The object’s unique ID. This is directly available, so does not need to be awaited in async sessions:
print(myobj.id)
- property kv¶
The key-value store associated with this object. For details of usage, see KV.
- Returns
A
heedy.kv.KV
object for the element.
- property meta¶
The object type’s metadata. For details of usage, see ObjectMeta. This does not need to be awaited in async sessions. Instead, to read the data if it was not yet cached, use
o.meta()
orawait o.meta()
depending on session type.- Returns
An
heedy.objects.objects.ObjectMeta
object for this element. See ObjectMeta.
- notifications¶
A
Notifications
object that allows you to access the notifications associated with this element. See Notifications for details.
- notify(*args, **kwargs)¶
Shorthand for
self.notifications.notify
(see Notifications).
- property owner¶
The user which owns this object:
print(o.owner.username) # Queries the server for the owner, and prints username print(o["owner"].username) # Uses cached query data to get the owner
print((await o.owner).username) # Queries the server for the owner, and prints username print(o["owner"].username) # Uses cached query data to get the owner
- Returns
The
User
object of the user which owns this object. The returned user does not have any cached data other than its username.
- read(**kwargs)¶
Sends a GET request to the element’s URI with function arguments as query parameters. This method is available for all subclasses of APIObject (objects, apps, users, etc), and is used to read element’s properties in heedy.
data = o.read(icon=True)
data = await o.read(icon=True)
Caches the result of the read accessible as dict keys:
assert data["name"] == o["name"]
The read or update functions both update the cached data automatically.
- Parameters
**kwargs – The url parameters to send with the request.
- Returns
The server’s response dict, namely a dict of the element’s properties.
- Raises
HeedyException – If the server returns an error.
- update(**kwargs)[source]¶
Sends a PATCH request to element’s URI with arguments as a json object. This method is available for all subclasses of APIObject (objects, apps, users, etc), and is used to update the element’s properties in heedy.
o.update(name="My new name",description="my new description") assert o["name"] == "My new name"
await o.update(name="My new name",description="my new description") assert o["name"] == "My new name"
- Parameters
**kwargs – The properties to update, sent as the json body of the request.
- Returns
The server’s response as a dict, namely the updated element’s properties.
- Raises
HeedyException – If the server returns an error, such as when there are insufficient permissions.
ObjectMeta¶
- class heedy.objects.objects.ObjectMeta(obj)[source]¶
Bases:
object
Heedy’s objects have a metadata field which stores object type-specific information. This
meta
property of an object is a key-value dictionary, and can be edited by altering the meta field in sync sessions, or by calling theupdate
method.# A timeseries type object has a schema key in its metadata. Setting "schema" here # does not alter any other elements of :code:`meta`, but only the "schema" key. o.meta = {"schema": {"type": "string"}}
# A timeseries type object has a schema key in its metadata. Setting "schema" here # does not alter any other elements of :code:`meta`, but only the "schema" key. await o.update(meta={"schema": {"type": "string"}})
The
meta
property behaves like a dictionary, but has features that help in usage. For example, the above code can be written as:o.meta.schema = {"type": "string"}
await o.meta.update(schema={"type": "string"})
The
meta
property also has several properties that offer syntactic sugar in synchronous code:del o.meta.schema # Resets the schema to its default value del o.meta["schema"] # Same as above len(o.meta) # Returns the number of keys currently cached in the object metadata "schema" in o.meta # Returns True if the schema key is in the cached meta field
await o.meta.delete("schema") # Resets the schema to its default value len(o.meta) # Returns the number of keys currently cached in the object metadata "schema" in o.meta # Returns True if the schema key is in the cached meta field
Finally, the semantics of
o.meta.schema
ando.meta["schema"]
are the same as for standard objects, meaning thato.meta["schema"]
does not query the server for the schema, but instead returns the cached values, whileo.meta.schema
will always query the server, and needs to be awaited in async sessions.- delete(*args)[source]¶
Delete the given keys from the object metadata.
Deleting a key resets the value of that property to its default. Removes the key from metadata if it is optional.
o.meta.delete("schema") assert o.meta["schema"] == {}
await o.meta.delete("schema") assert o.meta["schema"] == {}
- Parameters
*args – The keys to delete
- Returns
The updated object metadata
- Raises
HeedyException – If writing fails (usually due to insufficient permissions)
- update(**kwargs)[source]¶
Sets the given keys in the object’s type metadata.
o.meta.update(schema={"type": "string"})
await o.meta.update(schema={"type": "string"})
- Parameters
**kwargs – The keys to set and their values
- Returns
The updated object metadata (as a dictionary)
- Raises
HeedyException – If writing fails (usually due to insufficient permissions)
Timeseries¶
- class heedy.objects.timeseries.Timeseries(cached_data, session)[source]¶
Bases:
heedy.objects.objects.Object
- __call__(**kwargs)[source]¶
Returns the timeseries data matching the given constraints.
data = timeseries(t1="now-1h") # returns the last hour of data data = timeseries(t1="now-1h",output_type="dataframe") # returns the last hour of data, as a pandas dataframe data = timeseries(t1="jun 5",t2="jul 5",transform="sum") # returns the sum of data from jun 5th to jul 5th of this year.
data = await timeseries(t1="now-1h") # returns the last hour of data data = await timeseries(t1="now-1h",output_type="dataframe") # returns the last hour of data, as a pandas dataframe data = await timeseries(t1="jun 5",t2="jul 5",transform="sum") # returns the sum of data from jun 5th to jul 5th of this year.
- Parameters
t1 (float or str) – Only return datapoints with timestamp
>=t1
. Can be a unix timestamp, a string such as “last month”, “1pm” or “jun 5, 2019, 1pm” (any text supported by the dateparser library), or a relative time such as “now-1h” or “now-1d”.t2 (float or str) – Only return datapoints with timestamp
<t2
, with identical semantics to t1.t (float or str) – Return only the datapoint with the given exact timestamp (same semantics as t1)
i1 (int) – Only return datapoints with index
>=i1
(negative numbers are relative to the end of the timeseries).i2 (int) – Only return datapoints with index
<i2
, with identical semantics to i1.i (int) – Return only the datapoint with the given exact index (same semantics as i1).
limit (int) – The maximum number of datapoints to return.
transform (str) – The transform to apply to the data. See Transforms.
output_type (str) – The Python output type for this query. One of “list” or “dataframe”. By default,
Timeseries.output_type
is used.
- Returns
A
DatapointArray
or a pandas dataframe, depending on the value ofTimeseries.output_type
(oroutput_type
argument).- Raises
HeedyException – If the server returns an error.
- __getitem__(getrange)[source]¶
Allows accessing the timeseries just as if it were just one big python array.
#Returns the most recent 5 datapoints from the timeseries ts[-5:] #Returns all the data the timeseries holds. ts[:] # Returns the most recent datapoint ts[-1]
#Returns the most recent 5 datapoints from the timeseries await ts[-5:] #Returns all the data the timeseries holds. await ts[:] # Returns the most recent datapoint await ts[-1]
This is equivalent to calling
__call__
with i1 and i2 or i arguments.Note that if passed a string, it is equivalent to calling
Object[]
with the string, meaning that it gets the cached value of the prop:assert ts["name"]==ts.cached_data["name"]
- Returns
A
DatapointArray
or a pandas dataframe, depending on the value ofTimeseries.output_type
, or if passed string, the corresponding cached property value (seeObject.props
).- Raises
HeedyException – If the server returns an error.
- append(data, duration=0)[source]¶
Shorthand insert function, inserts the given data into the timeseries with the current timestamp.
ts.append("Hello World!")
await ts.append("Hello World!")
Equivalent to calling:
ts.insert(data,duration=duration)
- Parameters
data (json-convertible) – The value to insert
duration (float, optional) – The duration of the datapoint, in seconds.
- Raises
HeedyException – If the server returns an error.
- insert(data, timestamp=None, duration=0)[source]¶
Inserts the given data into the timeseries at the given timestamp.
ts.insert("Hello World!")
await ts.insert("Hello World!")
Equivalent to calling:
ts.insert_array([{"d": data, "t": timestamp,"dt":duration}])
- Parameters
data (json-convertible) – The value to insert
timestamp (float, optional) – The timestamp of the datapoint, in unix seconds. If none given, current time is used.
duration (float, optional) – The duration of the datapoint, in seconds.
- Raises
HeedyException – If the server returns an error.
- insert_array(datapoint_array, **kwargs)[source]¶
Given an array of datapoints in the heedy format, insert them into the timeseries.
ts.insert_array([ {"d": 4, "t": time.time()}, {"d": 5, "t": time.time(), "dt": 5.3} ])
await ts.insert_array([ {"d": 4, "t": time.time()}, {"d": 5, "t": time.time(), "dt": 5.3} ])
Heedy’s timeseries are indexed by timestamp, and datapoints in the series cannot have the same timestamp, or have overlapping durations. By default, heedy overwrites existing data with new data on insert when there is timestamp or duration overlap (update write method). When write method is set to insert, Heedy succeeds writing datapoints that don’t overlap, but fails if it would affect existing data. Finally, if set to append, only appending is permitted to the timeseries, meaning attempting to write datapoints before the most recent one will fail.
ts.insert_array([{"d": 4, "t": 123456}]) # Timeseries has 4 at the timestamp ts.insert_array([{"d": 5, "t": 123456}]) # The 4 was replaced with a 5 ts.insert_array([{"d": 6, "t": 123456}], method="update") # THROWS ERROR ts.insert_array([{"d": 7, "t": 123455}], method="update") # Succeeds (non-overlap) ts.insert_array([{"d": 8, "t": 123456}], method="append") # THROWS ERROR ts.insert_array([{"d": 9, "t": 123455}], method="append") # THROWS ERROR ts.insert_array([{"d": 10, "t": 123457}], method="append") # Succeeds (after existing)
await ts.insert_array([{"d": 4, "t": 123456}]) # Timeseries has 4 at the timestamp await ts.insert_array([{"d": 5, "t": 123456}]) # The 4 was replaced with a 5 await ts.insert_array([{"d": 6, "t": 123456}], method="update") # THROWS ERROR await ts.insert_array([{"d": 7, "t": 123455}], method="update") # Succeeds (non-overlap) await ts.insert_array([{"d": 8, "t": 123456}], method="append") # THROWS ERROR await ts.insert_array([{"d": 9, "t": 123455}], method="append") # THROWS ERROR await ts.insert_array([{"d": 10, "t": 123457}], method="append") # Succeeds (after existing)
- Parameters
datapoint_array (list) –
A list of dicts, with each dictionary having the following keys:
”d” (json-convertible): The datapoint value.
”t” (float): The timestamp of the datapoint, in unix seconds.
”dt” (float,optional): The duration of the datapoint, in seconds.
method (str, optional) –
The method to use when inserting datapoints. One of:
- ”update”
Insert datapoints, overwriting existing ones if they have the same timestamp or overlap.
- ”insert”
Insert datapoints, throwing an error if a timestamp conflicts with an existing one.
- ”append”
Only permit appends, meaning that no timestamp in the inserted array is <= any existing timestamp, and is < and existing timestamp+duration.
- Raises
HeedyException – If the server returns an error.
- length()[source]¶
Returns the number of datapoints in the timeseries.
len(timeseries) # equivalent to ts.length()
await ts.length()
- Returns
The number of datapoints in the timeseries.
- Raises
HeedyException – If the server returns an error.
- load(filename, **kwargs)[source]¶
Loads timeseries data JSON from the given file to the timeseries:
ts.load("myts.json")
await ts.load("myts.json")
This function can load data saved with the
save
function.- Raises
HeedyException – If the server returns an error.
- output_type = 'list'¶
A global property allowing you to specify the format in which timeseries data is returned by default. Can be one of:
“list” (default): A
DatapointArray
containing the data (i.e. a list of dicts), see DatapointArray.“dataframe”: A pandas dataframe containing the data. This is the default used in the heedy notebook interface, and is equivalent to using
to_df
on theDatapointArray
returned for the “list” type.
- remove(**kwargs)[source]¶
Removes all datapoints satisfying the given constraints.
ts.remove(t1="now-1h") # remove the last hour of data ts.remove(i=-1) # removes the most recent datapoint
await ts.remove(t1="now-1h") # remove the last hour of data await ts.remove(i=-1) # removes the most recent datapoint
- Parameters
t1 (float or str) – Only remove datapoints with timestamp
>=t1
. Can be a unix timestamp, a string such as “last month”, “1pm” or “jun 5, 2019, 1pm” (any text supported by the dateparser library), or a relative time such as “now-1h” or “now-1d”.t2 (float or str) – Only remove datapoints with timestamp
<t2
, with identical semantics to t1.t (float or str) – Remove only the datapoint with the given exact timestamp (same semantics as t1)
i1 (int) – Only remove datapoints with index
>=i1
(negative numbers are relative to the end of the timeseries).i2 (int) – Only remove datapoints with index
<i2
, with identical semantics to i1.i (int) – Remove only the datapoint with the given exact index (same semantics as i1).
- Raises
HeedyException – If the server returns an error.
DatapointArray¶
- class heedy.objects.timeseries.DatapointArray(data=[])[source]¶
Bases:
list
The DatapointArray is a convenience wrapper on data returned from timeseries. It allows a bit of extra functionality to make working with timeseries simpler.
- d()[source]¶
Returns just the data portion of the datapoints as a list:
DatapointArray([{"t": 12345, "d": "hi"}]).d() # ["hi"]
- dt()[source]¶
Returns a list of just the durations of all datapoints:
DatapointArray([ {"t": 12345, "d": "hi", "dt": 10}, {"t": 12346, "d": "hi"}, ]).dt() # [10,0]
- static load(filename)[source]¶
Adds the data from a JSON file. The file is expected to be in datapoint format:
d = DatapointArray.load("myfile.json")
Can be used to read data dumped by
save()
.
- mean()[source]¶
Gets the mean of the data portions of all datapoints within:
DatapointArray([ {"t": 12345, "d": 1}, {"t": 12346], "d": 2} ]).mean() # 1.5
- merge(array)[source]¶
Merges the current data with the given array. It assumes that the datapoints are formatted correctly for heedy, meaning that they are in the format:
[{"t": unix timestamp, "d": data,"dt": duration (optional)}]
The data does NOT need to be sorted by timestamp - this function sorts it for you
- raw()[source]¶
Returns array as a raw python list. For cases where for some reason the
DatapointArray
wrapper does not work for you.
- save(filename)[source]¶
Writes the data to the given file:
DatapointArray([{"t": unix timestamp, "d": data}]).save("myfile.json")
The data can later be loaded using load.
- sort(f=<function DatapointArray.<lambda>>)[source]¶
Sort the data in-place by the given function. Uses the timestamp by default.
- sum()[source]¶
Returns the sum of the data portions of all datapoints within:
DatapointArray([ {"t": 12345, "d": 1}, {"t": 12346], "d": 3.5} ]).sum() # 4.5
- t()[source]¶
Returns a list of just the timestamp portion of the datapoints. The timestamps are in python datetime’s date format:
DatapointArray([{"t": 12345, "d": "hi"}]).t() # [datetime.datetime(1969, 12, 31, 22, 25, 45)]
- to_df()[source]¶
Returns the data as a pandas dataframe. The dataframe has a “t” column that contains the timestamps as datetime objects. If the data has durations, there is a “dt” column as a timedelta.
Finally, if the data is a number, string, or boolean, there is a “d” column that contains the data. Otherwise, if the data portion is an object, the data has a column for each key, separated by “_”:
DatapointArray([ {"t": 12345, "d": {"a": 1, "b": 2}}, ]).to_df() # columns: t, d_a, d_b
- Returns
The data as a pandas dataframe.
- Return type
pandas.DataFrame
- tshift(t)[source]¶
Shifts all timestamps in the datapoint array by the given number of seconds. It is the same as the ‘tshift’ transform.
Warning: The shift is performed in-place! This means that it modifies the underlying array:
d = DatapointArray([{"t":56,"d":1}]) d.tshift(20) print(d) # [{"t":76,"d":1}]
- Parameters
t (float) – Number of seconds to shift the timestamps by.
- Returns
self, the shifted datapoint array.
Registering New Types¶
- heedy.objects.registry.registerObjectType(objectType, objectClass)[source]¶
If implementing a plugin which creates a new object type in Heedy, it might be useful to add support for the object type in the heedy python client. This is done by creating a subclass of
heedy.objects.Object
, and then registering it:from heedy.objects import Object,registerObjectType class MyType(Object): def myfunction(self): # Returns the result of a REST API call for the object return self.session.get(self.uri + "/mytype/my_rest_endpoint") registerObjectType("mytype",MyType)
Then, when reading objects, the object type is automatically detected and the correct class is used:
mtobjs = app.objects(type="mytype") for mtobj in mtobjs: print(mtobj.myfunction())
mtobjs = await app.objects(type="mytype") for mtobj in mtobjs: print(await mtobj.myfunction())