common/utils.py
changeset 0 b97547f5f1fa
child 28 9b7067bfaa15
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/utils.py	Wed Nov 05 15:52:50 2008 +0100
@@ -0,0 +1,257 @@
+"""Some utilities for CubicWeb server/clients.
+
+:organization: Logilab
+:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from md5 import md5
+from time import time
+from random import randint, seed
+
+# initialize random seed from current time
+seed()
+
+def make_uid(key):
+    """forge a unique identifier"""
+    msg = str(key) + "%.10f"%time() + str(randint(0, 1000000))
+    return md5(msg).hexdigest()
+
+def working_hours(mxdate):
+    """
+    Predicate returning True is the date's hour is in working hours (8h->20h)
+    """
+    if mxdate.hour > 7 and mxdate.hour < 21:
+        return True
+    return False
+    
+def date_range(begin, end, incr=1, include=None):
+    """yields each date between begin and end
+    :param begin: the start date
+    :param end: the end date
+    :param incr: the step to use to iterate over dates. Default is
+                 one day.                 
+    :param include: None (means no exclusion) or a function taking a
+                    date as parameter, and returning True if the date
+                    should be included.
+    """
+    date = begin
+    while date <= end:
+        if include is None or include(date): 
+            yield date
+        date += incr
+
+
+def dump_class(cls, clsname):
+    """create copy of a class by creating an empty class inheriting
+    from the given cls.
+
+    Those class will be used as place holder for attribute and relation
+    description
+    """
+    # type doesn't accept unicode name
+    # return type.__new__(type, str(clsname), (cls,), {})
+    # __autogenerated__ attribute is just a marker
+    return type(str(clsname), (cls,), {'__autogenerated__': True})
+
+
+def merge_dicts(dict1, dict2):
+    """update a copy of `dict1` with `dict2`"""
+    dict1 = dict(dict1)
+    dict1.update(dict2)
+    return dict1
+                
+
+class SizeConstrainedList(list):
+    """simple list that makes sure the list does not get bigger
+    than a given size.
+
+    when the list is full and a new element is added, the first
+    element of the list is removed before appending the new one
+
+    >>> l = SizeConstrainedList(2)
+    >>> l.append(1)
+    >>> l.append(2)
+    >>> l
+    [1, 2]
+    >>> l.append(3)
+    [2, 3]
+    """
+    def __init__(self, maxsize):
+        self.maxsize = maxsize
+
+    def append(self, element):
+        if len(self) == self.maxsize:
+            del self[0]
+        super(SizeConstrainedList, self).append(element)
+
+    def extend(self, sequence):
+        super(SizeConstrainedList, self).extend(sequence)
+        keepafter = len(self) - self.maxsize
+        if keepafter > 0:
+            del self[:keepafter]
+
+    __iadd__ = extend
+
+
+class UStringIO(list):
+    """a file wrapper which automatically encode unicode string to an encoding
+    specifed in the constructor
+    """
+
+    def __nonzero__(self):
+        return True
+    
+    def write(self, value):
+        assert isinstance(value, unicode), u"unicode required not %s : %s"\
+                                     % (type(value).__name__, repr(value))
+        self.append(value)
+        
+    def getvalue(self):
+        return u''.join(self)
+
+    def __repr__(self):
+        return '<%s at %#x>' % (self.__class__.__name__, id(self))
+
+
+class HTMLHead(UStringIO):
+    """wraps HTML header's stream
+
+    Request objects use a HTMLHead instance to ease adding of
+    javascripts and stylesheets
+    """
+    js_unload_code = u'jQuery(window).unload(unloadPageData);'
+
+    def __init__(self):
+        super(HTMLHead, self).__init__()
+        self.jsvars = []
+        self.jsfiles = []
+        self.cssfiles = []
+        self.ie_cssfiles = []
+        self.post_inlined_scripts = []
+        self.pagedata_unload = False
+
+
+    def add_raw(self, rawheader):
+        self.write(rawheader)
+
+    def define_var(self, var, value):
+        self.jsvars.append( (var, value) )
+
+    def add_post_inline_script(self, content):
+        self.post_inlined_scripts.append(content)
+    
+    def add_js(self, jsfile):
+        """adds `jsfile` to the list of javascripts used in the webpage
+
+        This function checks if the file has already been added
+        :param jsfile: the script's URL
+        """
+        if jsfile not in self.jsfiles:
+            self.jsfiles.append(jsfile)
+
+    def add_css(self, cssfile, media):
+        """adds `cssfile` to the list of javascripts used in the webpage
+
+        This function checks if the file has already been added
+        :param cssfile: the stylesheet's URL
+        """
+        if (cssfile, media) not in self.cssfiles:
+            self.cssfiles.append( (cssfile, media) )
+
+    def add_ie_css(self, cssfile, media='all'):
+        """registers some IE specific CSS"""
+        if (cssfile, media) not in self.ie_cssfiles:
+            self.ie_cssfiles.append( (cssfile, media) )
+
+    def add_unload_pagedata(self):
+        """registers onunload callback to clean page data on server"""
+        if not self.pagedata_unload:
+            self.post_inlined_scripts.append(self.js_unload_code)
+            self.pagedata_unload = True
+
+    def getvalue(self):
+        """reimplement getvalue to provide a consistent (and somewhat browser
+        optimzed cf. http://stevesouders.com/cuzillion) order in external
+        resources declaration
+        """
+        w = self.write
+        # 1/ variable declaration if any
+        if self.jsvars:
+            from simplejson import dumps
+            w(u'<script type="text/javascript">\n')
+            for var, value in self.jsvars:
+                w(u'%s = %s;\n' % (var, dumps(value)))
+            w(u'</script>\n')
+        # 2/ css files
+        for cssfile, media in self.cssfiles:
+            w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
+              (media, cssfile))
+        # 3/ ie css if necessary
+        if self.ie_cssfiles:
+            w(u'<!--[if lt IE 8]>\n')
+            for cssfile, media in self.ie_cssfiles:
+                w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
+                  (media, cssfile))
+            w(u'<![endif]--> \n')
+        # 4/ js files
+        for jsfile in self.jsfiles:
+            w(u'<script type="text/javascript" src="%s"></script>\n' % jsfile)
+        # 5/ post inlined scripts (i.e. scripts depending on other JS files)
+        if self.post_inlined_scripts:
+            w(u'<script type="text/javascript">\n')
+            w(u'\n\n'.join(self.post_inlined_scripts))
+            w(u'\n</script>\n')
+        return u'<head>\n%s</head>\n' % super(HTMLHead, self).getvalue()
+        
+
+class HTMLStream(object):
+    """represents a HTML page.
+
+    This is used my main templates so that HTML headers can be added
+    at any time during the page generation.
+    
+    HTMLStream uses the (U)StringIO interface to be compliant with
+    existing code.
+    """
+    
+    def __init__(self, req):
+        # stream for <head>
+        self.head = req.html_headers
+        # main stream
+        self.body = UStringIO()
+        self.doctype = u''
+        # xmldecl and html opening tag
+        self.xmldecl = u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding
+        self.htmltag = u'<html xmlns="http://www.w3.org/1999/xhtml" ' \
+                       'xmlns:cubicweb="http://www.logilab.org/2008/cubicweb" ' \
+                       'xml:lang="%s" lang="%s">' % (req.lang, req.lang)
+
+
+    def write(self, data):
+        """StringIO interface: this method will be assigned to self.w
+        """
+        self.body.write(data)
+
+    def getvalue(self):
+        """writes HTML headers, closes </head> tag and writes HTML body"""
+        return u'%s\n%s\n%s\n%s\n%s\n</html>' % (self.xmldecl, self.doctype,
+                                                 self.htmltag,
+                                                 self.head.getvalue(),
+                                                 self.body.getvalue())
+
+
+class AcceptMixIn(object):
+    """Mixin class for vobjects defining the 'accepts' attribute describing
+    a set of supported entity type (Any by default).
+    """
+    # XXX deprecated, no more necessary
+
+
+from logilab.common.deprecation import moved, class_moved
+rql_for_eid = moved('cubicweb.common.uilib', 'rql_for_eid')
+ajax_replace_url = moved('cubicweb.common.uilib', 'ajax_replace_url')
+
+import cubicweb
+Binary = class_moved(cubicweb.Binary)