Package Products :: Package ZenWidgets :: Module ZenTableManager |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenWidgets.ZenTableManager

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 or (at your
      8  # option) any later version as published by the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13
     14  __doc__="""ZenTableManager
     15
     16  ZenTableManager is a Zope Product that helps manage and display
     17  large sets of tabular data.  It allows for column sorting,
     18  break down of the set into pages, and filtering of elements
     19  in the table.  It also allows users to store their own default
     20  page size (but publishes a hook to get this values from
     21  a different location).
     22
     23
     24  $Id: ZenTableManager.py,v 1.4 2004/04/03 04:18:22 edahl Exp $"""
     25
     26  __revision__ = "$Revision: 1.4 $"[11:-2]
     27
     28  import re
     29  import ZTUtils
     30  from Globals import InitializeClass
     31  from Acquisition import aq_base
     32  from OFS.SimpleItem import SimpleItem
     33  from OFS.PropertyManager import PropertyManager
     34  from DocumentTemplate.sequence.SortEx import sort
     35
     36  from ZenTableState import ZenTableState
     37
     38
     39 -class TableStateNotFound(Exception): pass
     40
     41
     42 -def manage_addZenTableManager(context, id="", REQUEST = None):
     43      """make a CVDeviceLoader"""
     44      if not id: id = "ZenTableManager"
     45      ztm = ZenTableManager(id)
     46      context._setObject(id, ztm)
     47      ztm = context._getOb(id)
     48      ztm.initTableManagerSkins()
     49
     50      if REQUEST is not None:
     51          REQUEST.RESPONSE.redirect(context.absolute_url()
     52                                       +'/manage_main')
     53
     54 -class ZenTableManager(SimpleItem, PropertyManager):
     55      """ZenTableManager manages display of tabular data"""
     56
     57      portal_type = meta_type = 'ZenTableManager'
     58
     59      _properties = (
     60          {'id':'defaultBatchSize', 'type':'int','mode':'w'},
     61          {'id':'abbrStartLabel', 'type':'int','mode':'w'},
     62          {'id':'abbrEndLabel', 'type':'int','mode':'w'},
     63          {'id':'abbrPadding', 'type':'int','mode':'w'},
     64          {'id':'abbrSeparator', 'type':'string','mode':'w'},
     65      )
     66
     67      manage_options = (
     68                          PropertyManager.manage_options +
     69                          SimpleItem.manage_options
     70                       )
     71
     72
     73 -    def __init__(self, id):
     74          self.id = id
     75          self.defaultBatchSize = 40
     76          self.abbrStartLabel = 15
     77          self.abbrEndLabel = 5
     78          self.abbrPadding = 5
     79          self.abbrSeparator = ".."
     80          self.abbrThresh = self.abbrStartLabel + \
     81                          self.abbrEndLabel + self.abbrPadding
     82
     83
     84 -    def getDefaultBatchSize(self):
     85          dbs = self.defaultBatchSize
     86          zu = getattr(self, "ZenUsers", None)
     87          if zu and zu.getUserSettings():
     88              dbs = zu.getUserSettings().defaultPageSize
     89          return dbs
     90
     91
     92 -    def setupTableState(self, tableName, **keys):
     93          """initialize or setup the session variable to track table state"""
     94          tableState = self.getTableState(tableName, **keys)
     95          request = self.REQUEST
     96          tableState.updateFromRequest(request)
     97          return tableState
     98
     99
    100 -    def getTableState(self, tableName, attrname=None, default=None, **keys):
    101          """return an existing table state or a single value from the state"""
    102          from Products.ZenUtils.Utils import unused
    103          unused(default)
    104          request = self.REQUEST
    105          tableStates = self.getTableStates()
    106          tableState = tableStates.get(tableName, None)
    107          if not tableState:
    108              dbs = self.getDefaultBatchSize()
    109              tableStates[tableName] = ZenTableState(request,tableName,dbs,**keys)
    110              tableState = tableStates[tableName]
    111          if attrname == None:
    112              return tableStates[tableName]
    113          return getattr(tableState, attrname, None)
    114
    115
    116 -    def getReqTableState(self, tableName, attrname):
    117          """
    118          Return attrname from request if present if not return from tableState.
    119          """
    120          request = self.REQUEST
    121          if request.has_key(attrname):
    122              return request[attrname]
    123          return self.getTableState(tableName, attrname)
    124
    125
    126 -    def setTableState(self, tableName, attrname, value):
    127          """Set the value of a table state attribute and return it."""
    128          tableState = self.getTableState(tableName)
    129          return tableState.setTableState(attrname, value)
    130
    131
    132 -    def setReqTableState(self, tableName, attrname, default=None, reset=False):
    133          """set the a value in the table state from the request"""
    134          tableState = self.getTableState(tableName)
    135          value = self.REQUEST.get(attrname, None)
    136          tableState = self.getTableState(tableName)
    137          return tableState.setTableState(attrname, value,
    138                                          default=default, reset=reset)
    139
    140
    141 -    def getBatch(self, tableName, objects, **keys):
    142          """Filter, sort and batch objects and pass return set.
    143          """
    144          if not objects:
    145              objects = []
    146          tableState = self.setupTableState(tableName, **keys)
    147          if tableState.onlyMonitored and objects:
    148              objects = [o for o in objects if getattr(o, 'isMonitored', o.monitored)()]
    149          if tableState.filter and objects:
    150              objects = self.filterObjects(objects, tableState.filter,
    151                                          tableState.filterFields)
    152          # objects is frequently a generator.  Need a list in order to sort
    153          if not isinstance(objects, list):
    154              objects = list(objects)
    155          if tableState.sortedHeader:
    156              objects = self.sortObjects(objects, tableState)
    157          tableState.totalobjs = len(objects)
    158          tableState.buildPageNavigation(objects)
    159          if not hasattr(self.REQUEST, 'doExport'):
    160              objects = ZTUtils.Batch(objects,
    161                          tableState.batchSize or len(objects),
    162                          start=tableState.start, orphan=0)
    163          return objects
    164
    165
    166 -    def getBatchForm(self, objects, request):
    167          """Create batch based on objects no sorting for filter applied.
    168          """
    169          batchSize = request.get('batchSize',self.defaultBatchSize)
    170          if batchSize in ['', '0']:
    171              batchSize = 0
    172          else:
    173              batchSize = int(batchSize)
    174          start = int(request.get('start',0))
    175          resetStart = int(request.get('resetStart',0))
    176          lastindex = request.get('lastindex',0)
    177          navbutton = request.get('navbutton',None)
    178          if navbutton == "first" or resetStart:
    179              start = 0
    180          elif navbutton == "last":
    181              start=lastindex
    182          elif navbutton == "next":
    183              start = start + batchSize
    184              if start > lastindex: start = lastindex
    185          elif navbutton == "prev":
    186              start = start - batchSize
    187          elif request.has_key("nextstart"):
    188              start = request.nextstart
    189          if 0 < start > len(objects): start = 0
    190          request.start = start
    191          objects = ZTUtils.Batch(objects, batchSize or len(objects),
    192                      start=request.start, orphan=0)
    193          return objects
    194
    195
    196 -    def filterObjects(self, objects, regex, filterFields):
    197          """filter objects base on a regex in regex and list of fields
    198          in filterFields."""
    199          if self.REQUEST.SESSION.has_key('message'):
    200              self.REQUEST.SESSION.delete('message')
    201          if not regex:
    202              return objects
    203          try: search = re.compile(regex,re.I).search
    204          except re.error:
    205              self.REQUEST.SESSION['message'] = "Invalid regular expression."
    206              return objects
    207          filteredObjects = []
    208          for obj in objects:
    209              target = []
    210              for field in filterFields:
    211                  if isinstance(obj, dict):
    212                      value = obj.get(field, None)
    213                  else:
    214                      value = getattr(obj, field, None)
    215                  if callable(value):
    216                      value = value()
    217                  if not isinstance(value, basestring):
    218                      value = str(value)
    219                  target.append(value)
    220              targetstring = " ".join(target)
    221              if search(targetstring): filteredObjects.append(obj)
    222          return filteredObjects
    223
    224
    225 -    def sortObjects(self, objects, request):
    226          """Sort objects.
    227          """
    228          def dictAwareSort(objects, field, rule, sence):
    229              if not objects:
    230                  return objects
    231              class Wrapper:
    232                  def __init__(self, field, cargo):
    233                      if callable(field): field = field()
    234                      self.field = field
    235                      self.cargo = cargo
    236              if isinstance(objects[0], dict):
    237                  objects = [Wrapper(o.get(field, ''), o) for o in objects]
    238              else:
    239                  objects = [Wrapper(getattr(o, field, ''), o) for o in objects]
    240              objects = sort(objects, (('field', rule, sence),))
    241              return [w.cargo for w in objects]
    242
    243          if (getattr(aq_base(request), 'sortedHeader', False)
    244              and getattr(aq_base(request),"sortedSence", False)):
    245              sortedHeader = request.sortedHeader
    246              sortedSence = request.sortedSence
    247              sortRule = getattr(aq_base(request), "sortRule", "cmp")
    248              objects = dictAwareSort(objects, sortedHeader, sortRule, sortedSence)
    249          return objects
    250
    251
    252 -    def getTableHeader(self, tableName, fieldName, fieldTitle,
    253                  sortRule='cmp', style='tableheader',attributes=""):
    254          """generate a <th></th> tag that allows column sorting"""
    255          href = self.getTableHeaderHref(tableName, fieldName, sortRule)
    256          style = self.getTableHeaderStyle(tableName, fieldName, style)
    257          tag = """<th class="%s" %s>""" % (style, attributes)
    258          tag += """<a class="%s" href="%s""" % (style, href)
    259          tag += fieldTitle + "</a></th>\n"
    260          return tag
    261
    262
    263 -    def getTableHeaderHref(self, tableName, fieldName,
    264                              sortRule='cmp',params=""):
    265          """build the href attribute for the table table headers"""
    266
    267          tableState = self.getTableState(tableName)
    268          sortedHeader = tableState.sortedHeader
    269          sortedSence = tableState.sortedSence
    270          if sortedHeader == fieldName:
    271              if sortedSence == 'asc':
    272                  sortedSence = 'desc'
    273              elif sortedSence == 'desc':
    274                  fieldName = ''
    275                  sortedSence = ''
    276          else:
    277              sortedSence = 'asc'
    278          href = "%s?tableName=%s&sortedHeader=%s&" % (
    279                  self.REQUEST.URL, tableName, fieldName)
    280          href += "sortedSence=%s&sortRule=%s%s\">" % (
    281                  sortedSence, sortRule, params)
    282          tableState.addFilterField(fieldName)
    283          return href
    284
    285
    286 -    def getTableHeaderStyle(self, tableName, fieldName, style):
    287          """apends "selected" onto the CSS style if this field is selected"""
    288          if self.getTableState(tableName, "sortedHeader") == fieldName:
    289              style = style + "selected"
    290          return style
    291
    292
    293 -    def getTableStates(self):
    294          session = self.REQUEST.SESSION
    295          try:
    296              return session['zentablestates']
    297          except KeyError:
    298              init = {}
    299              session['zentablestates'] = init
    300              return init
    301
    302
    303 -    def tableStatesHasTable(self, tableName):
    304          return self.getTableStates().has_key(tableName)
    305
    306
    307 -    def getNavData(self, objects, batchSize, sortedHeader):
    308          pagenav = []
    309          if batchSize in ['', '0']:
    310              batchSize = 0
    311          else:
    312              batchSize = int(batchSize)
    313          for index in range(0, len(objects), batchSize or len(objects)):
    314              if sortedHeader:
    315                  label = self._buildTextLabel(objects[index], sortedHeader)
    316              elif batchSize:
    317                  label = str(1+index/batchSize)
    318              else:
    319                  label = '1'
    320              pagenav.append({ 'label': label, 'index': index })
    321          return pagenav
    322
    323
    324 -    def _buildTextLabel(self, item, sortedHeader):
    325          startAbbr = ""
    326          endAbbr = ""
    327          attr = getattr(item, sortedHeader, "")
    328          if callable(attr): attr = attr()
    329          label = str(attr)
    330          if len(label) > self.abbrThresh:
    331              startAbbr = label[:self.abbrStartLabel]
    332              if self.abbrEndLabel > 0:
    333                  endAbbr = label[-self.abbrEndLabel:]
    334              label = "".join((startAbbr, self.abbrSeparator, endAbbr))
    335          return label
    336
    337
    338 -    def initTableManagerSkins(self):
    339          """setup the skins that come with ZenTableManager"""
    340          layers = ('zentablemanager','zenui')
    341          try:
    342              import string
    343              from Products.CMFCore.utils import getToolByName
    344              from Products.CMFCore.DirectoryView import addDirectoryViews
    345              skinstool = getToolByName(self, 'portal_skins')
    346              for layer in layers:
    347                  if layer not in skinstool.objectIds():
    348                      addDirectoryViews(skinstool, 'skins', globals())
    349              skins = skinstool.getSkinSelections()
    350              for skin in skins:
    351                  path = skinstool.getSkinPath(skin)
    352                  path = map(string.strip, string.split(path,','))
    353                  for layer in layers:
    354                      if layer not in path:
    355                          try:
    356                              path.insert(path.index('custom')+1, layer)
    357                          except ValueError:
    358                              path.append(layer)
    359                  path = ','.join(path)
    360                  skinstool.addSkinSelection(skin, path)
    361          except ImportError, e:
    362              if "Products.CMFCore.utils" in e.args: pass
    363              else: raise
    364          except AttributeError, e:
    365              if "portal_skin" in e.args: pass
    366              else: raise
    367
    368
    369  InitializeClass(ZenTableManager)
    370