1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
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
65
69
72
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
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
95 if attributes['editable']:
96
97
98
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
122 """
123 :return: the number of invalid rows in a model, as they have been verified
124 """
125 return len(self._invalid_rows)
126
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
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
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