Package Camelot :: Package camelot :: Package admin :: Package validator :: Module object_validator |
[frames] | no frames]

# Source Code for Module Camelot.camelot.admin.validator.object_validator

      1  #  ============================================================================
      2  #
      3  #  Copyright (C) 2007-2008 Conceptive Engineering bvba. All rights reserved.
      4  #  www.conceptive.be / project-camelot@conceptive.be
      5  #
      6  #  This file is part of the Camelot Library.
      7  #
      8  #  This file may be used under the terms of the GNU General Public
      9  #  License version 2.0 as published by the Free Software Foundation
     10  #  and appearing in the file LICENSE.GPL included in the packaging of
     11  #  this file.  Please review the following information to ensure GNU
     12  #  General Public Licensing requirements will be met:
     13  #  http://www.trolltech.com/products/qt/opensource.html
     14  #
     15  #  If you are unsure which license is appropriate for your use, please
     16  #  review the following information:
     17  #  http://www.trolltech.com/products/qt/licensing.html or contact
     18  #  project-camelot@conceptive.be.
     19  #
     20  #  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
     21  #  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     22  #
     23  #  For use of this library in commercial applications, please contact
     24  #  project-camelot@conceptive.be
     25  #
     26  #  ============================================================================
     27
     28  from copy import copy
     29  import logging
     30  logger = logging.getLogger('camelot.admin.validator.object_validator')
     31
     32  from PyQt4 import QtCore
     33
     34  from camelot.view.fifo import fifo
     35  from camelot.view.model_thread import post
     36
     37 -class ObjectValidator(QtCore.QObject):
     38      """A validator class for normal python objects.  By default this validator
     39      declares all objects valid.  Subclass this class and overwrite it's
     40      objectValidity method to change it's behaviour.
     41      """
     42
     43      validity_changed_signal = QtCore.SIGNAL('validityChanged')
     44
     45 -    def __init__(self, admin, model, initial_validation=False):
     46          """
     47          :param verifiy_initial_validity: do an inital check to see if all rows in a model are valid, defaults to False,
     48          since this might take a lot of time on large collections.
     49          """
     50          super(ObjectValidator, self).__init__()
     51          self.admin = admin
     52          self.model = model
     53          self.message_cache = fifo(10)
     54          self.connect( model, QtCore.SIGNAL('dataChanged(const QModelIndex &, const QModelIndex &)'), self.data_changed )
     55          self.connect( model, QtCore.SIGNAL('layoutChanged()'), self.layout_changed )
     56          self._invalid_rows = set()
     57
     58          if initial_validation:
     59              post(self.validate_all_rows)
     60
     61 -    def validate_all_rows(self):
     62          """Force validation of all rows in the model"""
     63          for row in range(self.model.getRowCount()):
     64              self.isValid(row)
     65
     66 -    def validate_invalid_rows(self):
     67          for row in copy(self._invalid_rows):
     68              self.isValid(row)
     69
     70 -    def layout_changed(self):
     71          post(self.validate_invalid_rows)
     72
     73 -    def data_changed(self, from_index, thru_index):
     74
     75          def create_validity_updater(from_row, thru_row):
     76
     77              def validity_updater():
     78                  for i in range(from_row, thru_row+1):
     79                      self.isValid(i)
     80
     81              return validity_updater
     82
     83          post(create_validity_updater(from_index.row(), thru_index.row()))
     84
     85 -    def objectValidity(self, entity_instance):
     86          """:return: list of messages explaining invalid data
     87          empty list if object is valid
     88          """
     89          from camelot.view.controls import delegates
     90          messages = []
     91          fields_and_attributes = dict(self.admin.get_columns())
     92          fields_and_attributes.update(dict(self.admin.get_fields()))
     93          for field, attributes in fields_and_attributes.items():
     94              # if the field was not editable, don't waste any time
     95              if attributes['editable']:
     96                  # if the field, is nullable, don't waste time getting its value
     97                  #@todo: check if field is a primary key instead of checking
     98                  # whether the name is id, but this should only happen in the entity validator
     99                  if attributes['nullable']!=True and field!='id':
    100                      value = getattr(entity_instance, field)
    101                      logger.debug('column %s is required'%(field))
    102                      if 'delegate' not in attributes:
    103                          raise Exception('no delegate specified for %s'%(field))
    104                      is_null = False
    105                      if value==None:
    106                          is_null = True
    107                      elif (attributes['delegate'] == delegates.CodeDelegate) and \
    108                           (sum(len(c) for c in value) == 0):
    109                          is_null = True
    110                      elif (attributes['delegate'] == delegates.PlainTextDelegate) and (len(value) == 0):
    111                          is_null = True
    112                      elif (attributes['delegate'] == delegates.Many2OneDelegate) and (not value.id):
    113                          is_null = True
    114                      elif (attributes['delegate'] == delegates.VirtualAddressDelegate) and (not value[1]):
    115                          is_null = True
    116                      if is_null:
    117                          messages.append(u'%s is a required field' % (attributes['name']))
    118          logger.debug(u'messages : %s'%(u','.join(messages)))
    119          return messages
    120
    121 -    def number_of_invalid_rows(self):
    122          """
    123          :return: the number of invalid rows in a model, as they have been verified
    124          """
    125          return len(self._invalid_rows)
    126
    127 -    def isValid(self, row):
    128          """Verify if a row in a model is 'valid' meaning it could be flushed to
    129          the database
    130          """
    131          messages = []
    132          logger.debug('isValid for row %s' % row)
    133          try:
    134              entity_instance = self.model._get_object(row)
    135              if entity_instance:
    136                  messages = self.objectValidity(entity_instance)
    137                  self.message_cache.add_data(row, entity_instance, messages)
    138          except Exception, e:
    139              logger.error(
    140                  'programming error while validating object',
    141                  exc_info=e
    142              )
    143          valid = (len(messages) == 0)
    144          if not valid:
    145              if row not in self._invalid_rows:
    146                  self._invalid_rows.add(row)
    147                  self.emit(self.validity_changed_signal, row)
    148          elif row in self._invalid_rows:
    149              self._invalid_rows.remove(row)
    150              self.emit(self.validity_changed_signal, row)
    151          logger.debug('valid : %s' % valid)
    152          return valid
    153
    154 -    def validityMessages(self, row):
    155          try:
    156              return self.message_cache.get_data_at_row(row)
    157          except KeyError:
    158              raise Exception(
    159                  'Programming error : isValid should be called '
    160                  'before calling validityMessage'
    161              )
    162
    163 -    def validityDialog(self, row, parent):
    164          """Return a QDialog that asks the user to discard his changes or
    165          continue to edit the row until it is valid.
    166          """
    167          from PyQt4 import QtGui
    168          from camelot.core.utils import ugettext as _
    169          return QtGui.QMessageBox(
    170              QtGui.QMessageBox.Warning,
    171              _('Invalid form'),
    172              '\n'.join(self.validityMessages(row)),
    173              QtGui.QMessageBox.Ok | QtGui.QMessageBox.Discard,
    174              parent
    175          )
    176