from typing import Dict
import pprint
from ..base import APIObject, APIList, Session, q
from ..kv import KV
from .. import users
from .. import apps
from ..notifications import Notifications
from . import registry
class Object(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 = {
"name",
"description",
"icon",
"access",
"meta",
"tags",
"key",
"owner_scope",
}
def __init__(self, cached_data: Dict, session: Session):
super().__init__(
f"api/objects/{q(cached_data['id'])}",
{},
session,
cached_data=cached_data,
)
#: A :class:`~heedy.notifications.Notifications` object that allows you to access the notifications
#: associated with this element. See :ref:`python_notifications` for details.
self.notifications = Notifications({"object": cached_data["id"]}, self.session)
self._kv = KV(f"api/kv/objects/{q(cached_data['id'])}", self.session)
@property
def id(self):
"""
The object's unique ID. This is directly available, so does not need
to be awaited in async sessions::
print(myobj.id)
"""
return self.cached_data["id"]
@property
def kv(self):
"""
The key-value store associated with this object. For details of usage, see :ref:`python_kv`.
Returns:
A :class:`heedy.kv.KV` object for the element.
"""
return self._kv
@kv.setter
def kv(self, v):
return self._kv.set(**v)
@property
def meta(self):
"""
The object type's metadata. For details of usage, see :ref:`python_objectmeta`.
This does not need to be awaited in async sessions. Instead, to read the data if it was
not yet cached, use :code:`o.meta()` or :code:`await o.meta()` depending on session type.
Returns:
An :class:`heedy.objects.objects.ObjectMeta` object for this element. See :ref:`python_objectmeta`.
"""
return ObjectMeta(self)
@meta.setter
def meta(self, v):
return self.update(meta=v)
def __getitem__(self, i):
if i == "meta":
return self.meta
v = super().__getitem__(i)
if i == "owner":
return users.User(v, self.session)
if i == "app" and v is not None:
return apps.App(v, session=self.session)
return v
@property
def owner(self):
"""
The user which owns this object:
.. tab:: Sync
::
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
.. tab:: Async
::
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 :class:`~heedy.User` object of the user which owns this object.
The returned user does not have any cached data other than its username.
"""
return self.session.f(
self.read(), lambda x: users.User(x["owner"], self.session)
)
@property
def app(self):
"""
The app that manages this object, if any:
.. tab:: Sync
::
if obj.app is not None: # Query the server for the object's app
print(obj["app"].id) # Use cached value from previous query
.. tab:: Async
::
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 :class:`~heedy.App` object does not have any cached data other than its id, so you will need to call
:code:`read()` on it if accessing more than its id.
"""
return self.session.f(
self.read(),
lambda x: apps.App(x["app"], session=self.session)
if x["app"] is not None
else None,
)
def update(self, **kwargs):
meta = self.cached_data.get("meta", {})
def updateMeta(o):
if "result" in o and o["result"] == "ok" and "meta" in kwargs:
if kwargs["meta"] is None:
self.cached_data.pop("meta", 0)
else:
# We have values of meta, so set them correctly
kwm = kwargs["meta"]
for key in kwm:
if kwm[key] is None:
meta.pop(key, 0)
else:
meta[key] = kwm[key]
self.cached_data["meta"] = meta
return o
return self.session.f(super().update(**kwargs), updateMeta)
class Objects(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.
.. tab:: Sync
::
myuser.objects() # objects belonging to myuser
myapp.objects() # objects managed by myapp
plugin.objects() # all objects in heedy
.. tab:: Async
::
await myuser.objects() # objects belonging to myuser
await myapp.objects() # objects managed by myapp
await plugin.objects() # all objects in heedy
"""
def __init__(self, constraints: Dict, session: Session):
super().__init__("api/objects", constraints, session)
def __getitem__(self, objectId: str):
"""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.
.. tab:: Sync
::
obj = p.objects["d233rk43o6kkle43kl"]
.. tab:: Async
::
obj = await p.objects["d233rk43o6kkle43kl"]
Returns:
The :class:`~heedy.Object` with the given ID (or promise for the object)
Throws:
HeedyException: If the object does not exist
"""
return super()._getitem(
objectId, f=lambda x: registry.getObject(x, self.session)
)
def __call__(self, **kwargs):
"""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).
.. tab:: Sync
::
obj = p.objects(type="timeseries",
key="mykey",
tags="tag1 tag2",
owner="myuser",
app="")
.. tab:: Async
::
obj = await p.objects(type="timeseries",
key="mykey",
tags="tag1 tag2",
owner="myuser",
app="")
Args:
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 :class:`~heedy.Object` matching the given constraints.
Throws:
HeedyException: If the request fails.
"""
return super()._call(
kwargs, f=lambda x: [registry.getObject(xx, self.session) for xx in x]
)
def create(self, name: str, meta: Dict = {}, type: str = "timeseries", **kwargs):
"""
Creates a new object of the given type (timeseries by default).
Only the first argument, the object name is required.
.. tab:: Sync
::
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")
.. tab:: Async
::
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.
Args:
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 :class:`~heedy.Object`, with its data cached.
Throws:
HeedyException: if the object could not be created.
"""
return super()._create(
{"name": name, "type": type, "meta": meta, **kwargs},
f=lambda x: registry.getObject(x, self.session),
)