views:

316

answers:

3

It looks like this has been covered somewhat in other questions, but I'm still fairly confused on how to actually do this. My lack of experience isn't helping much with that.

I have two DateTimeProperties - StartTime and EndTime. I'm subtracting StartTime from EndTime to get the Duration. From my previous question (thank you to all that answered!) it looks like this operation is producing a timedelta.

There doesn't seem to be an easy way to store timedelta directly in the GAE datastore, so this means I need to convert it either to an int in milliseconds, to a float in seconds or to time.

I will need to do other calculations on this later as well, such as figuring out avg. duration. Based on that, int seems to make the most sense to me right now.

What's the best way to do this or is there a tutorial I can play with?

Thank you!

A: 
import pickle
import datetime

...

delta = end_time - start_time
for_storage = pickle.dumps(delta)
#now you have a string representation of your timedelta object that you can store

#sometime later...
delta = pickle.loads(from_storage)

You'll still need to convert the delta to a time resolution of your choice using the days, mins, seconds, and microseconds attributes of the time delta.

Brian Luft
must have done something wrong - it unpickled datetime.timedelta...
Sologoub
yeah, really doing something wrong - tried storing it as a string, that didn't work either:raise BadValueError('Property %s is not multi-line' % self.name)10.BadValueError: Property Duration is not multi-line
Sologoub
Ouch, no. This will be slow, and the value will be unindexable.
Nick Johnson
A: 

This ultimately worked:

delta = StartTime - EndTime

event_record.Duration = int((delta.microseconds)/1000)

basically, needed to get microseconds out of the timedelta and convert it to milliseconds.

Sologoub
Don't do that! timedelta has 3 fields, (days, seconds, microseconds), and you're only considering the microseconds fields. Timedeltas of 0:0:0.5, 0:0:30.5, and 1:2:3.5 will all be recorded identically.
Nick Johnson
Nick thank you! That explains a few things!
Sologoub
+2  A: 

To make this as easy as possible to work with, there's two steps: Converting the timedelta to an int or a float, and storing it in the datastore. First things first, converting a timedelta to a microtime:

def timedelta_to_microtime(td):
  return td.microseconds + (td.seconds + td.days * 86400) * 1000000

You don't have to do the conversion yourself, though - you can define a custom datastore property, which will allow you to store timedeltas directly to your model:

class TimeDeltaProperty(db.Property):
  def get_value_for_datastore(self, model_instance):
    value = self.__get__(model_instance, model_instance.__class__)
    if value is not None:
      return timedelta_to_microtime(value)

  def make_value_from_datastore(self, value):
    if value is not None:
      return datetime.timedelta(microseconds=value)

Now you can use this property like any other:

class MyModel(db.Model):
  td = TimeDeltaProperty(required=True)

entity = MyModel(td=datetime.datetime.now()-some_datetime)
key = entity.put()

entity = db.get(key)
print entity.td
Nick Johnson
So is this the right way to make it into milliseconds? event_record.Duration = int((delta.microseconds + (delta.seconds + delta.days * 86400) * 1000000)/1000)
Sologoub
I think your `TimeDeltaProperty` needs to define a `data_type` too. Second, I'm not sure what your `__get__` does, but the article on extending datastore properties suggests `super(TimeDeltaProperty, self).get_value_for_datastore(model_instance)`
Noio
@Sologoub Nearly - just remove the /1000 and delete 4 zeroes from the multiplier. :)@Noio data_type isn't required - and nor is __get__ - the default getter and setter just store and retrieve the value as-is.
Nick Johnson
You sure? Cause I'm first converting to microseconds, then adding the microseconds from delta to converted seconds and days and then converting to milliseconds.
Sologoub
@Sologoub How about delta.microseconds / 1000 + (delta.seconds + delta.days * 86400) * 1000
Nick Johnson
That works too :)Also, found this very helpful in creating datetime.time out of timedelta: http://stackoverflow.com/questions/764184/python-how-do-i-get-time-from-a-datetime-timedelta-object
Sologoub