view roundup/support.py @ 3635:53987aa153d2

Transitive-property support. - Fixed some of the pet-peeves from pep8 - Better parameter names for new _subselect method - use new-style class for support.Proptree but needed a new-style class for the property I introduced anyway. - Fix a bug where searching did the wrong thing (interestingly enough the same wrong thing for all backends): A search for {'messages': ['1'], 'messages.author': ['2']} would ignore the 'messages' part (messages being non-leaf node in proptree). Fixed and added a regression test for this. - Added the transitive searching to the SearchAction. New method get_transitive_prop introduced in hyperdb that does the transitive version of getprops()[name]. Fixed two tests to use the (faked) method instead of getprop. Now searching for transitive props via the web-interface works for me. Thanks to alexander smishlajev for pointing me at the coding style. Sorry for stepping on the peeves -- I'm using a different coding style in most other projects I'm doing ...
author Ralf Schlatterbeck <schlatterbeck@users.sourceforge.net>
date Thu, 13 Jul 2006 10:14:56 +0000
parents 57c66056ffe4
children fa7becc62534
line wrap: on
line source

"""Implements various support classes and functions used in a number of
places in Roundup code.
"""

__docformat__ = 'restructuredtext'

import os, time, sys
import hyperdb

from sets import Set

class TruthDict:
    '''Returns True for valid keys, False for others.
    '''
    def __init__(self, keys):
        if keys:
            self.keys = {}
            for col in keys:
                self.keys[col] = 1
        else:
            self.__getitem__ = lambda name: 1

    def __getitem__(self, name):
        return self.keys.has_key(name)

def ensureParentsExist(dest):
    if not os.path.exists(os.path.dirname(dest)):
        os.makedirs(os.path.dirname(dest))

class PrioList:
    '''Manages a sorted list.

    Currently only implements method 'append' and iteration from a
    full list interface.
    Implementation: We manage a "sorted" status and sort on demand.
    Appending to the list will require re-sorting before use.
    >>> p = PrioList()
    >>> for i in 5,7,1,-1:
    ...  p.append(i)
    ...
    >>> for k in p:
    ...  print k
    ...
    -1
    1
    5
    7

    '''
    def __init__(self):
        self.list   = []
        self.sorted = True

    def append(self, item):
        self.list.append(item)
        self.sorted = False

    def __iter__(self):
        if not self.sorted:
            self.list.sort()
            self.sorted = True
        return iter(self.list)

class Progress:
    '''Progress display for console applications.

    See __main__ block at end of file for sample usage.
    '''
    def __init__(self, info, sequence):
        self.info = info
        self.sequence = iter(sequence)
        self.total = len(sequence)
        self.start = self.now = time.time()
        self.num = 0
        self.stepsize = self.total / 100 or 1
        self.steptimes = []
        self.display()

    def __iter__(self): return self

    def next(self):
        self.num += 1

        if self.num > self.total:
            print self.info, 'done', ' '*(75-len(self.info)-6)
            sys.stdout.flush()
            return self.sequence.next()

        if self.num % self.stepsize:
            return self.sequence.next()

        self.display()
        return self.sequence.next()

    def display(self):
        # figure how long we've spent - guess how long to go
        now = time.time()
        steptime = now - self.now
        self.steptimes.insert(0, steptime)
        if len(self.steptimes) > 5:
            self.steptimes.pop()
        steptime = sum(self.steptimes) / len(self.steptimes)
        self.now = now
        eta = steptime * ((self.total - self.num)/self.stepsize)

        # tell it like it is (or might be)
        if now - self.start > 3:
            M = eta / 60
            H = M / 60
            M = M % 60
            S = eta % 60
            if self.total:
                s = '%s %2d%% (ETA %02d:%02d:%02d)'%(self.info,
                    self.num * 100. / self.total, H, M, S)
            else:
                s = '%s 0%% (ETA %02d:%02d:%02d)'%(self.info, H, M, S)
        elif self.total:
            s = '%s %2d%%'%(self.info, self.num * 100. / self.total)
        else:
            s = '%s %d done'%(self.info, self.num)
        sys.stdout.write(s + ' '*(75-len(s)) + '\r')
        sys.stdout.flush()

class Proptree(object):
    ''' Simple tree data structure for optimizing searching of properties
    '''

    def __init__(self, db, cls, name, props, parent = None, val = None):
        self.db = db
        self.name = name
        self.props = props
        self.parent = parent
        self._val = val
        self.cls = cls
        self.classname = None
        self.uniqname = None
        self.children = []
        self.propnames = {}
        if parent:
            self.root = parent.root
            self.prcls = self.parent.props [name]
        else:
            self.root = self
            self.seqno = 1
        self.id = self.root.seqno
        self.root.seqno += 1
        if self.cls:
            self.classname = self.cls.classname
            self.uniqname = '%s%s' % (self.cls.classname, self.id)
        if not self.parent:
            self.uniqname = self.cls.classname

    def append(self, name):
        """Append a property to self.children. Will create a new
        propclass for the child.
        """
        if name in self.propnames:
            return self.propnames [name]
        propclass = self.props [name]
        cls = None
        props = None
        if isinstance(propclass, (hyperdb.Link, hyperdb.Multilink)):
            cls = self.db.getclass(propclass.classname)
            props = cls.getprops()
        child = self.__class__(self.db, cls, name, props, parent = self)
        self.children.append(child)
        self.propnames [name] = child
        return child

    def _set_val(self, val):
        """Check if self._val is already defined. If yes, we compute the
        intersection of the old and the new value(s)
        """
        if self._val:
            v = self._val
            if not isinstance(self._val, type([])):
                v = [self._val]
            vals = Set(v)
            vals.intersection_update(val)
            self._val = [v for v in vals]
        else:
            self._val = val
    
    val = property(lambda self: self._val, _set_val)

    def __iter__(self):
        """ Yield nodes in depth-first order -- visited nodes first """
        for p in self.children:
            yield p
            for c in p:
                yield c

# vim: set et sts=4 sw=4 :

Roundup Issue Tracker: http://roundup-tracker.org/