Skip to content
This repository has been archived by the owner on Aug 4, 2022. It is now read-only.

Commit

Permalink
added timeseries support
Browse files Browse the repository at this point in the history
  • Loading branch information
lsbardel committed Dec 9, 2010
1 parent 301ed21 commit 64eb5d0
Show file tree
Hide file tree
Showing 21 changed files with 223 additions and 210 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.rst
@@ -1,8 +1,10 @@
Ver. 0.4.3 - Development
===========================
* More documentation and tests.
* Included support for redis ``timeseries`` which requires redis fork at https://github.com/lsbardel/redis.
* Added ``contrib.sessions`` module for handling web sessions. Experimental and pre-alpha.
* Added :class:`stdnet.orm.JSONField` with tests.
* **64 tests**.
* **64 tests** (75 including timeseries) with **53% coverage**.

Ver. 0.4.2 - 2010 Nov 17
============================
Expand Down
9 changes: 9 additions & 0 deletions README.rst
Expand Up @@ -74,6 +74,14 @@ otherwise from the package directory::
**BE WARNED! RUNNING TESTS WILL DESTROY ANYTHING IN LOCALHOST REDIS DATABASE 13. MAKE SURE YOU DONT HAVE ANYTHING ON DATABASE 13 OTHERWISE FOLLOW INSTRUCTIONS BELOW**


To access coverage of tests you need to install the coverage_ package and run the tests using::

coverage run --source=stdnet runtests.py
and to check out the coverage report::

coverage report -m

Default settings
=========================
Expand Down Expand Up @@ -201,3 +209,4 @@ file in the top distribution directory for the full license text.
.. _Memcached: http://memcached.org/
.. _BSD: http://www.opensource.org/licenses/bsd-license.php
.. _Sphinx: http://sphinx.pocoo.org/
.. _coverage: http://nedbatchelder.com/code/coverage/
8 changes: 8 additions & 0 deletions runtests.py
Expand Up @@ -20,12 +20,20 @@ def makeoptions():
dest="server",
default='',
help="Backend server where to run tests")
parser.add_option("-i", "--include",
action="store",
dest="itags",
default='',
help="Include develepment tags, comma separated")
return parser


if __name__ == '__main__':
options, tags = makeoptions().parse_args()
server = options.server
itags = options.itags.replace(' ','')
itags = None if not itags else itags.split(',')
stdnet.runtests(tags,
itags=itags,
verbosity=options.verbosity,
backend=options.server)
4 changes: 2 additions & 2 deletions stdnet/__init__.py
Expand Up @@ -36,13 +36,13 @@ def setup_tests(backend = 'redis://127.0.0.1:6379/?db=13'):
add2path()


def runtests(tags = None, verbosity = 1, backend = None):
def runtests(tags = None, itags = None, verbosity = 1, backend = None):
from stdnet.conf import settings
backend = backend or 'redis://127.0.0.1:6379/?db=13'
std = settings.DEFAULT_BACKEND
setup_tests(backend)
from stdnet.tests.runtests import run
run(tags = tags, verbosity = verbosity)
run(tags = tags, itags = itags, verbosity = verbosity)
settings.DEFAULT_BACKEND = std


Expand Down
4 changes: 2 additions & 2 deletions stdnet/backends/base.py
Expand Up @@ -276,10 +276,10 @@ def hash(self, id, timeout = 0, **kwargs):
for a given *id*.'''
return self.structure_module.HashTable(self, id, timeout = timeout, **kwargs)

def map(self, id, timeout = 0, **kwargs):
def ts(self, id, timeout = 0, **kwargs):
'''Return an instance of :class:`stdnet.HashTable` structure
for a given *id*.'''
return self.structure_module.Map(self, id, timeout = timeout, **kwargs)
return self.structure_module.TS(self, id, timeout = timeout, **kwargs)

def unordered_set(self, id, timeout = 0, **kwargs):
'''Return an instance of :class:`stdnet.Set` structure
Expand Down
47 changes: 29 additions & 18 deletions stdnet/backends/structures/base.py
@@ -1,8 +1,5 @@
'''Interfaces for supported data-structures'''

from stdnet.utils import listPipeline, mapPipeline


__all__ = ['PipeLine',
'pipelines',
'Structure',
Expand All @@ -14,6 +11,25 @@
default_score = lambda x : 1



class listPipeline(object):
def __init__(self):
self.clear()

def push_front(self, value):
self.front.append(value)

def push_back(self, value):
self.back.append(value)

def clear(self):
self.back = []
self.front = []

def __len__(self):
return len(self.back) + len(self.front)


class keyconverter(object):

@classmethod
Expand All @@ -39,9 +55,9 @@ class HashPipe(PipeLine):
def __init__(self, timeout):
super(HashPipe,self).__init__({},'hash',timeout)

class MapPipe(PipeLine):
class TsPipe(PipeLine):
def __init__(self, timeout):
super(MapPipe,self).__init__(mapPipeline(),'map',timeout)
super(TsPipe,self).__init__({},'ts',timeout)

class SetPipe(PipeLine):
def __init__(self, timeout):
Expand All @@ -58,7 +74,7 @@ def __init__(self, timeout):

_pipelines = {'list':ListPipe,
'hash': HashPipe,
'map': MapPipe,
'ts': TsPipe,
'set': SetPipe,
'oset': OsetPipe}

Expand Down Expand Up @@ -409,21 +425,18 @@ def _mget(self, keys):
raise NotImplementedError


class Map(HashTable):
struct = MapPipe
class TS(HashTable):
struct = TsPipe

def __init__(self, *args, **kwargs):
self.scorefun = kwargs.pop('scorefun',default_score)
super(Map,self).__init__(*args, **kwargs)
super(TS,self).__init__(*args, **kwargs)

def update(self, mapping):
'''Add *mapping* dictionary to hashtable. Equivalent to python dictionary update method.'''
tokey = self.converter.tokey
dumps = self.pickler.dumps
sfunc = self.scorefun
p = self.pipeline
for key,value in mapping.iteritems():
rk = tokey(key)
p[rk] = dumps(value)
p[tokey(key)] = dumps(value)

def front(self):
try:
Expand All @@ -441,15 +454,13 @@ def range(self, start, end):
'''Return a range between start and end key.'''
tokey = self.converter.tokey
tovalue = self.converter.tovalue
sfunc = self.scorefun
loads = self.pickler.loads
for key,val in self._range(sfunc(tokey(start)),sfunc(tokey(end))):
for key,val in self._range(tokey(start),tokey(end)):
yield tovalue(key),loads(val)

def count(self, start, end):
tokey = self.converter.tokey
sfunc = self.scorefun
return self._count(sfunc(tokey(start)),sfunc(tokey(end)))
return self._count(tokey(start),tokey(end))

def irange(self, start = 0, end = -1):
'''Return a range between start and end key.'''
Expand Down
13 changes: 5 additions & 8 deletions stdnet/backends/structures/structredis.py
Expand Up @@ -144,7 +144,7 @@ def add_expiry(self):
self.cursor.execute_command('EXPIRE', self.id, self.timeout)


class Map(structures.Map):
class TS(structures.TS):
'''Requires Redis Map structure which is not yet implemented in redis (and I don't know if it will).
It is implemented on my redis-fork at https://github.com/lsbardel/redis'''
def _size(self):
Expand All @@ -163,19 +163,16 @@ def delete(self, key):
return self.cursor.execute_command('TSDEL', self.id, key)

def _contains(self, key):
if self.cursor.execute_command('TSEXISTS', self.id, key):
return True
else:
return False
return self.cursor.execute_command('TSEXISTS', self.id, key)

def _getdate(self, val):
return None if not val else val[0]

def _irange(self, start, end):
return riteritems(self, 'TSRANGE', start, end, 'withscores')
return riteritems(self, 'TSRANGE', start, end, 'withtimes')

def _range(self, start, end):
return riteritems(self, 'TSRANGEBYSCORE', start, end, 'withscores')
return riteritems(self, 'TSRANGEBYTIME', start, end, 'withtimes')

def _count(self, start, end):
return self.cursor.execute_command('TSCOUNT', self.id, start, end)
Expand All @@ -190,7 +187,7 @@ def _keys(self):
return self.cursor.execute_command('TSRANGE', self.id, 0, -1, 'novalues')

def _items(self):
return riteritems(self, 'TSRANGE', self.id, 0, -1, 'withscores')
return riteritems(self, 'TSRANGE', 0, -1, 'withtimes')

def values(self):
for ky,val in self.items():
Expand Down
22 changes: 12 additions & 10 deletions stdnet/contrib/timeserie/models.py
Expand Up @@ -2,6 +2,7 @@
from stdnet.utils import date2timestamp, timestamp2date, todatetime, todate
from stdnet.contrib.timeserie.utils import default_parse_interval


class DateTimeConverter(object):

@classmethod
Expand All @@ -12,6 +13,7 @@ def tokey(cls, value):
def tovalue(cls, value):
return timestamp2date(value)


class DateConverter(object):

@classmethod
Expand All @@ -30,18 +32,18 @@ def register_with_model(self, name, model):
self.converter = model.converter


class TimeSerieMapField(orm.MapField):

class TimeSeriesField(orm.TSField):
'''A new timeseries field based on TS data structure in Redis'''
def __init__(self, *args, **kwargs):
kwargs['scorefun'] = lambda x : x
super(TimeSerieMapField,self).__init__(*args, **kwargs)
super(TimeSeriesField,self).__init__(*args, **kwargs)

def register_with_model(self, name, model):
super(TimeSerieMapField,self).register_with_model(name, model)
super(TimeSeriesField,self).register_with_model(name, model)
self.converter = model.converter


class TimeSeriesBase(orm.StdModel):
'''Timeseries base class'''
converter = DateTimeConverter

def todate(self, v):
Expand Down Expand Up @@ -101,8 +103,8 @@ def intervals(self, startdate, enddate, parseinterval = default_parse_interval):
return calc_intervals


class TimeSeriesMap(TimeSeriesBase):
data = TimeSerieMapField()
class TimeSeries(TimeSeriesBase):
data = TimeSeriesField()

def dates(self):
return self.data.keys()
Expand All @@ -119,7 +121,7 @@ def __get_end(self):
end = property(__get_end)


class TimeSeries(TimeSeriesBase):
class HashTimeSeries(TimeSeriesBase):
'''Base abstract class for timeseries'''
data = TimeSerieField()
start = orm.DateTimeField(required = False, index = False)
Expand All @@ -132,7 +134,7 @@ def items(self):
return self.data.sorteditems()

def save(self):
supersave = super(TimeSeries,self).save
supersave = super(HashTimeSeries,self).save
supersave()
self.storestartend()
return supersave()
Expand All @@ -157,7 +159,7 @@ def __str__(self):
return self.fromto()


class DateTimeSeries(TimeSeries):
class DateHashTimeSeries(HashTimeSeries):
converter = DateConverter
start = orm.DateField(required = False, index = False)
end = orm.DateField(required = False, index = False)
Expand Down
4 changes: 2 additions & 2 deletions stdnet/contrib/timeserie/tests/__init__.py
@@ -1,2 +1,2 @@
from test_timeseries_hash import *
#from test_timeseries import *
from hashtimeseries import *
from ts import *
Expand Up @@ -6,7 +6,7 @@
from stdnet.contrib.timeserie.utils import dategenerator, default_parse_interval
from stdnet.utils import populate, todate

from models import TimeSeries, DateTimeSeries
from .models import HashTimeSeries, DateHashTimeSeries


NUM_DATES = 300
Expand All @@ -20,9 +20,8 @@
testdata2 = dict(alldata2)


class TestTimeSeriesHash(TestCase):
tags = ['timeserie','hash']
model = TimeSeries
class TestHashTimeSeries(TestCase):
model = HashTimeSeries
mkdate = datetime

def setUp(self):
Expand Down Expand Up @@ -198,8 +197,8 @@ def testGet(self):
self.assertEqual(ts.data.get(mkdate(2010,3,1)),None)


class TestDateTimeSeriesHash(TestTimeSeriesHash):
model = DateTimeSeries
class TestDateHashTimeSeries(TestHashTimeSeries):
model = DateHashTimeSeries
mkdate = date


7 changes: 4 additions & 3 deletions stdnet/contrib/timeserie/tests/models.py
Expand Up @@ -3,14 +3,15 @@
from stdnet.contrib.timeserie import models


class DateTimeSeries(models.DateTimeSeries):
class HashTimeSeries(models.HashTimeSeries):
ticker = orm.SymbolField(unique = True)


class TimeSeries(models.TimeSeries):
class DateHashTimeSeries(models.DateHashTimeSeries):
ticker = orm.SymbolField(unique = True)


class TimeSeriesMap(models.TimeSeriesMap):
class TimeSeries(models.TimeSeries):
ticker = orm.SymbolField(unique = True)


@@ -1,18 +1,18 @@
from itertools import izip
from datetime import date, datetime

import test_timeseries_hash
from models import TimeSeriesMap
import hashtimeseries
from models import TimeSeries

testdata = test_timeseries_hash.testdata
testdata2 = test_timeseries_hash.testdata2
testdata = hashtimeseries.testdata
testdata2 = hashtimeseries.testdata2



class TestDateTimeSeriesTS(test_timeseries_hash.TestTimeSeriesHash):
tags = ['timeserie','ts']
class TestDateTimeSeriesTS(hashtimeseries.TestHashTimeSeries):
tag = 'ts'
default_run = False
model = TimeSeriesMap
model = TimeSeries

def testitems2(self):
ts = self.filldata(testdata2)
Expand Down
8 changes: 8 additions & 0 deletions stdnet/lib/__init__.py
@@ -0,0 +1,8 @@
try:
from stdlib import *
hasextensions = True
except:
hasextensions = False
from .fallback import *
else:
import fallback

0 comments on commit 64eb5d0

Please sign in to comment.