common/utils.py
changeset 0 b97547f5f1fa
child 28 9b7067bfaa15
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 """Some utilities for CubicWeb server/clients.
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 """
       
     7 __docformat__ = "restructuredtext en"
       
     8 
       
     9 from md5 import md5
       
    10 from time import time
       
    11 from random import randint, seed
       
    12 
       
    13 # initialize random seed from current time
       
    14 seed()
       
    15 
       
    16 def make_uid(key):
       
    17     """forge a unique identifier"""
       
    18     msg = str(key) + "%.10f"%time() + str(randint(0, 1000000))
       
    19     return md5(msg).hexdigest()
       
    20 
       
    21 def working_hours(mxdate):
       
    22     """
       
    23     Predicate returning True is the date's hour is in working hours (8h->20h)
       
    24     """
       
    25     if mxdate.hour > 7 and mxdate.hour < 21:
       
    26         return True
       
    27     return False
       
    28     
       
    29 def date_range(begin, end, incr=1, include=None):
       
    30     """yields each date between begin and end
       
    31     :param begin: the start date
       
    32     :param end: the end date
       
    33     :param incr: the step to use to iterate over dates. Default is
       
    34                  one day.                 
       
    35     :param include: None (means no exclusion) or a function taking a
       
    36                     date as parameter, and returning True if the date
       
    37                     should be included.
       
    38     """
       
    39     date = begin
       
    40     while date <= end:
       
    41         if include is None or include(date): 
       
    42             yield date
       
    43         date += incr
       
    44 
       
    45 
       
    46 def dump_class(cls, clsname):
       
    47     """create copy of a class by creating an empty class inheriting
       
    48     from the given cls.
       
    49 
       
    50     Those class will be used as place holder for attribute and relation
       
    51     description
       
    52     """
       
    53     # type doesn't accept unicode name
       
    54     # return type.__new__(type, str(clsname), (cls,), {})
       
    55     # __autogenerated__ attribute is just a marker
       
    56     return type(str(clsname), (cls,), {'__autogenerated__': True})
       
    57 
       
    58 
       
    59 def merge_dicts(dict1, dict2):
       
    60     """update a copy of `dict1` with `dict2`"""
       
    61     dict1 = dict(dict1)
       
    62     dict1.update(dict2)
       
    63     return dict1
       
    64                 
       
    65 
       
    66 class SizeConstrainedList(list):
       
    67     """simple list that makes sure the list does not get bigger
       
    68     than a given size.
       
    69 
       
    70     when the list is full and a new element is added, the first
       
    71     element of the list is removed before appending the new one
       
    72 
       
    73     >>> l = SizeConstrainedList(2)
       
    74     >>> l.append(1)
       
    75     >>> l.append(2)
       
    76     >>> l
       
    77     [1, 2]
       
    78     >>> l.append(3)
       
    79     [2, 3]
       
    80     """
       
    81     def __init__(self, maxsize):
       
    82         self.maxsize = maxsize
       
    83 
       
    84     def append(self, element):
       
    85         if len(self) == self.maxsize:
       
    86             del self[0]
       
    87         super(SizeConstrainedList, self).append(element)
       
    88 
       
    89     def extend(self, sequence):
       
    90         super(SizeConstrainedList, self).extend(sequence)
       
    91         keepafter = len(self) - self.maxsize
       
    92         if keepafter > 0:
       
    93             del self[:keepafter]
       
    94 
       
    95     __iadd__ = extend
       
    96 
       
    97 
       
    98 class UStringIO(list):
       
    99     """a file wrapper which automatically encode unicode string to an encoding
       
   100     specifed in the constructor
       
   101     """
       
   102 
       
   103     def __nonzero__(self):
       
   104         return True
       
   105     
       
   106     def write(self, value):
       
   107         assert isinstance(value, unicode), u"unicode required not %s : %s"\
       
   108                                      % (type(value).__name__, repr(value))
       
   109         self.append(value)
       
   110         
       
   111     def getvalue(self):
       
   112         return u''.join(self)
       
   113 
       
   114     def __repr__(self):
       
   115         return '<%s at %#x>' % (self.__class__.__name__, id(self))
       
   116 
       
   117 
       
   118 class HTMLHead(UStringIO):
       
   119     """wraps HTML header's stream
       
   120 
       
   121     Request objects use a HTMLHead instance to ease adding of
       
   122     javascripts and stylesheets
       
   123     """
       
   124     js_unload_code = u'jQuery(window).unload(unloadPageData);'
       
   125 
       
   126     def __init__(self):
       
   127         super(HTMLHead, self).__init__()
       
   128         self.jsvars = []
       
   129         self.jsfiles = []
       
   130         self.cssfiles = []
       
   131         self.ie_cssfiles = []
       
   132         self.post_inlined_scripts = []
       
   133         self.pagedata_unload = False
       
   134 
       
   135 
       
   136     def add_raw(self, rawheader):
       
   137         self.write(rawheader)
       
   138 
       
   139     def define_var(self, var, value):
       
   140         self.jsvars.append( (var, value) )
       
   141 
       
   142     def add_post_inline_script(self, content):
       
   143         self.post_inlined_scripts.append(content)
       
   144     
       
   145     def add_js(self, jsfile):
       
   146         """adds `jsfile` to the list of javascripts used in the webpage
       
   147 
       
   148         This function checks if the file has already been added
       
   149         :param jsfile: the script's URL
       
   150         """
       
   151         if jsfile not in self.jsfiles:
       
   152             self.jsfiles.append(jsfile)
       
   153 
       
   154     def add_css(self, cssfile, media):
       
   155         """adds `cssfile` to the list of javascripts used in the webpage
       
   156 
       
   157         This function checks if the file has already been added
       
   158         :param cssfile: the stylesheet's URL
       
   159         """
       
   160         if (cssfile, media) not in self.cssfiles:
       
   161             self.cssfiles.append( (cssfile, media) )
       
   162 
       
   163     def add_ie_css(self, cssfile, media='all'):
       
   164         """registers some IE specific CSS"""
       
   165         if (cssfile, media) not in self.ie_cssfiles:
       
   166             self.ie_cssfiles.append( (cssfile, media) )
       
   167 
       
   168     def add_unload_pagedata(self):
       
   169         """registers onunload callback to clean page data on server"""
       
   170         if not self.pagedata_unload:
       
   171             self.post_inlined_scripts.append(self.js_unload_code)
       
   172             self.pagedata_unload = True
       
   173 
       
   174     def getvalue(self):
       
   175         """reimplement getvalue to provide a consistent (and somewhat browser
       
   176         optimzed cf. http://stevesouders.com/cuzillion) order in external
       
   177         resources declaration
       
   178         """
       
   179         w = self.write
       
   180         # 1/ variable declaration if any
       
   181         if self.jsvars:
       
   182             from simplejson import dumps
       
   183             w(u'<script type="text/javascript">\n')
       
   184             for var, value in self.jsvars:
       
   185                 w(u'%s = %s;\n' % (var, dumps(value)))
       
   186             w(u'</script>\n')
       
   187         # 2/ css files
       
   188         for cssfile, media in self.cssfiles:
       
   189             w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
       
   190               (media, cssfile))
       
   191         # 3/ ie css if necessary
       
   192         if self.ie_cssfiles:
       
   193             w(u'<!--[if lt IE 8]>\n')
       
   194             for cssfile, media in self.ie_cssfiles:
       
   195                 w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
       
   196                   (media, cssfile))
       
   197             w(u'<![endif]--> \n')
       
   198         # 4/ js files
       
   199         for jsfile in self.jsfiles:
       
   200             w(u'<script type="text/javascript" src="%s"></script>\n' % jsfile)
       
   201         # 5/ post inlined scripts (i.e. scripts depending on other JS files)
       
   202         if self.post_inlined_scripts:
       
   203             w(u'<script type="text/javascript">\n')
       
   204             w(u'\n\n'.join(self.post_inlined_scripts))
       
   205             w(u'\n</script>\n')
       
   206         return u'<head>\n%s</head>\n' % super(HTMLHead, self).getvalue()
       
   207         
       
   208 
       
   209 class HTMLStream(object):
       
   210     """represents a HTML page.
       
   211 
       
   212     This is used my main templates so that HTML headers can be added
       
   213     at any time during the page generation.
       
   214     
       
   215     HTMLStream uses the (U)StringIO interface to be compliant with
       
   216     existing code.
       
   217     """
       
   218     
       
   219     def __init__(self, req):
       
   220         # stream for <head>
       
   221         self.head = req.html_headers
       
   222         # main stream
       
   223         self.body = UStringIO()
       
   224         self.doctype = u''
       
   225         # xmldecl and html opening tag
       
   226         self.xmldecl = u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding
       
   227         self.htmltag = u'<html xmlns="http://www.w3.org/1999/xhtml" ' \
       
   228                        'xmlns:cubicweb="http://www.logilab.org/2008/cubicweb" ' \
       
   229                        'xml:lang="%s" lang="%s">' % (req.lang, req.lang)
       
   230 
       
   231 
       
   232     def write(self, data):
       
   233         """StringIO interface: this method will be assigned to self.w
       
   234         """
       
   235         self.body.write(data)
       
   236 
       
   237     def getvalue(self):
       
   238         """writes HTML headers, closes </head> tag and writes HTML body"""
       
   239         return u'%s\n%s\n%s\n%s\n%s\n</html>' % (self.xmldecl, self.doctype,
       
   240                                                  self.htmltag,
       
   241                                                  self.head.getvalue(),
       
   242                                                  self.body.getvalue())
       
   243 
       
   244 
       
   245 class AcceptMixIn(object):
       
   246     """Mixin class for vobjects defining the 'accepts' attribute describing
       
   247     a set of supported entity type (Any by default).
       
   248     """
       
   249     # XXX deprecated, no more necessary
       
   250 
       
   251 
       
   252 from logilab.common.deprecation import moved, class_moved
       
   253 rql_for_eid = moved('cubicweb.common.uilib', 'rql_for_eid')
       
   254 ajax_replace_url = moved('cubicweb.common.uilib', 'ajax_replace_url')
       
   255 
       
   256 import cubicweb
       
   257 Binary = class_moved(cubicweb.Binary)