xively / xively-python

The official pythonic wrapper library for the Xively™ API

Home Page:http://xively.github.io/xively-python/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

feed.datastreams should maybe be a dict type object not a sequence?

smulube opened this issue · comments

This is a more general point. Currently the datastreams attribute on a feed instance is a Sequence like thing, which I think lets you do stuff like:

feed.datastreams[0].current_value = "12"
feed.datastreams[1].current_value = "49.2"

feed.update()

however I think that datastreams attribute should be a dict type thing, with the stream id as the key, i.e. I'd like to be able to do this:

feed.datastreams["external_temp"].current_value = "12"
feed.datastreams["cpu_temp"].current_value = "49.2"

feed.update()

Thoughts @gnublade? I started looking at implementing this, but quickly got out of my Python depth ;)

Yes, you are probably right. I have been considering this and seems somewhat more useful but it has always been my aim to replicate the API as closely as possible, therefore since the response returns an array and not a mapping I didn't make it a Mapping type. There is another concern that there is a slight overhead of converting it to a dict that would be done on every request for a feed.

The code for this should be simple enough, although we might want to only perform the conversion if the datastreams are accessed, however this also goes against my other "do no magic" rule.

Heh, it might be it's not the best way of doing it; consider these just suggestions/comments obviously.

So when I was trying to use the library (in the little Raspberry Pi tutorial I wrote for the site), I was trying to do exactly the little example I gave above, i.e. I had a feed object with a couple of datastreams; then the script looped continuously updating each of the datastreams at regular intervals.

How would you approach that with the current library? Would you keep a reference to each of the datastreams and just update each of them (although that seems like it would end up making multiple API calls which seems a bit inefficient), or would you just access the datastreams by numerical index, or is there another way of doing that, that I haven't seen?

What about something like:

# either
feed.update({"external_temp": 12, "cpu_temp": 49.2}, ts=optional_shared_timestamp);
# or
# a) timestamped version:
feed.update(external_temp=(12, ts1), cpu_temp=(49.2, ts2));
# b) non-timestamped:
feed.update(external_temp=12, cpu_temp=49.2);

It might actually make sense to have helpers module for some typical use cases, one would be the feed update like this.
So those kind of shortcuts like the above can be defined in the helper module... would that make sense? Then the code might look like this:

# either
updateHelper(feed, {"external_temp": 12, "cpu_temp": 49.2}, ts=optional_shared_timestamp));
# or
# a) timestamped version:
updateHelper(feed, external_temp=(12, ts1), cpu_temp=(49.2, ts2));
# b) non-timestamped:
updateHelper(feed, external_temp=12, cpu_temp=49.2);

In fact if it's a helper method, then may be timestamp is not required at all, it just gets local time when the helper is called.

Just thinking...

The point is thought that the users will keep writing their helper methods for indexing datastreams, may be they shouldn't need to write that each time and just import our helpers instead?

Once silly user called @errordeveloper wrote this:

import cosm
from bbio import *
from time import sleep
import resource
from datetime import datetime, timedelta
from os import environ

cosm_api = cosm.CosmAPIClient(environ['COSM_API_KEY'])
cosm_feed = cosm_api.feeds.get(environ['COSM_FEED_ID'])

def roundFloat(x):
  return round(float(x), 2)

def collectMeasurments():
  return [
    cosm.Datastream(id='AnaloguePin1Voltage', current_value=roundFloat(inVolts(analogRead(AIN1))), tags=["voltage"]),
  ]

class DebugDatastreams:
  def __init__(self):
    self.start_time = datetime.now()
    self.tags = [ 'debug' ]
  def all(self):
   return self.program_resource_usage() + [
     self.system_uptime_datastream(),
     self.program_uptime_datastream(),
   ]
  def system_uptime_datastream(self):
    with open('/proc/uptime', 'r') as proc_uptime:
      current_time = datetime.now()
      uptime_seconds = roundFloat(proc_uptime.readline().split()[0])
      return cosm.Datastream(id='debug.uptime.system', current_value=uptime_seconds, at=current_time, tags=self.tags)
  def program_uptime_datastream(self):
    current_time = datetime.now()
    uptime_seconds = roundFloat((current_time - self.start_time).total_seconds())
    return cosm.Datastream(id='debug.uptime.program', current_value=uptime_seconds, at=current_time, tags=self.tags)
  def program_resource_usage(self):
    current_time = datetime.now()
    ru = resource.getrusage(resource.RUSAGE_SELF)
    return [
      cosm.Datastream(id='debug.program.resource_usage.system_time', current_value=ru.ru_stime, at=current_time, tags=self.tags),
      cosm.Datastream(id='debug.program.resource_usage.user_time', current_value=ru.ru_utime, at=current_time, tags=self.tags),
    ]


debug_datastreams = DebugDatastreams()

while True:
  cosm_feed.datastreams = collectMeasurments() + debug_datastreams.all()
  cosm_feed.update()
  sleep(10)

He thinks that constructing a sequence of cosm.Datastream's is quite okay, however he's not quite sure.