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):
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