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" # Uses only cached read data
# 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 # Suppose meta={"a": 1,"b":2,"c":3}
obj.meta = {"c": 4,"b": None}
obj.meta # {"a": 1,"c":4}
await obj.meta # Suppose meta={"a": 1,"b":2,"c":3}
await obj.update(meta={"c": 4,"b": None})
await obj.meta # {"a": 1,"c":4}

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)         # 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).

The following constraints are supported:

  • type: The type of the objects (like “timeseries”)

  • key: The unique app key of the object (each app can only have one object with a given key)

  • tags: The tags that the object must have, separated by spaces

  • owner: The owner username of the object (set automatically when accessed using the objects property of a user)

  • app: 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)

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="")
Returns

A list of objects 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.

Returns

The newly created object, with its data cached.

Throws:

HeedyException: if the object could not be created.

Object

class heedy.Object(objectData, 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. If you want to used cached data instead of o.name, use o["name"], and call o.read() to update the cache.

property app

The app that manages this object, if any:

if obj.app is not None:
    print(obj.app.id)
if obj.app is not None:
    print(obj.app.id) # no need to await id prop in this case, because id is already cached

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 call read() on it if accessing its cached data.

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 KV object for the element. See KV.

property meta

The object type’s metadata. For details of usage, see ObjectMeta.

Returns

An 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.user.username) # prints the username of the object's owner
Returns

The User object (see User) of the user which owns this object, with username cached (read() will need to be called on the user to get other properties).

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 as attributes of the object:

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 the update 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 and o.meta["schema"] are the same as for standard objects, meaning that o.meta["schema"] does not query the server for the schema, but instead returns the cached values, while o.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(objectData, 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 of Timeseries.output_type (or output_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 of Timeseries.output_type, or if passed string, the corresponding cached property value (see Object.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 the DatapointArray 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.

save(filename)[source]

Saves the entire timeseries data as JSON to the given filename:

ts.save("myts.json")
await ts.save("myts.json")

The data can then be loaded using DatapointArray.load().

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())