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 """
29 Python structures to represent filters.
30 These structures can be transformed to QT forms.
31 """
32
33 from camelot.view.model_thread import gui_function
34 from camelot.core.utils import ugettext_lazy as _
37 """Convert a python data structure to a filter, using the following rules :
38
39 if structure is an instance of Filter, return structure
40 else create a GroupBoxFilter from the structure
41 """
42 if isinstance(structure, Filter):
43 return structure
44 return GroupBoxFilter(structure)
45
47 """Base class for filters"""
48
49 - def __init__(self, attribute, value_to_string=lambda x:unicode(x)):
50 """
51 @param attribute: the attribute on which to filter, this attribute
52 may contain dots to indicate relationships that need to be followed,
53 eg. 'person.groups.name'
54 @param value_to_string: function that converts a value of the attribute to
55 a string that will be displayed in the filter
56 """
57 self.attribute = attribute
58 self._value_to_string = value_to_string
59
60 @gui_function
61 - def render(self, parent, name, options):
62 """Render this filter as a qt object
63 @param parent: its parent widget
64 @param name: the name of the filter
65 @param options: the options that can be selected, where each option is a list
66 of tuples containting (option_name, query_decorator)
67
68 The name and the list of options can be fetched with get_name_and_options"""
69 raise NotImplementedError()
70
72 """return a tuple of the name of the filter and a list of options that can be selected.
73 Each option is a tuple of the name of the option, and a filter function to
74 decorate a query
75 @return: (filter_name, [(option_name, query_decorator), ...)
76 """
77 from sqlalchemy.sql import select
78 from sqlalchemy import orm
79 from elixir import session
80 filter_names = []
81 joins = []
82 table = admin.entity.table
83 path = self.attribute.split('.')
84 for field_name in path:
85 attributes = admin.get_field_attributes(field_name)
86 filter_names.append(attributes['name'])
87
88 if 'target' in attributes:
89 admin = attributes['admin']
90 joins.append(field_name)
91 if attributes['direction'] == orm.interfaces.MANYTOONE:
92 table = admin.entity.table.join(table)
93 else:
94 table = admin.entity.table
95
96
97 col = getattr(admin.entity, field_name)
98 query = select([col], distinct=True, order_by=col.asc()).select_from(table)
99
100 def create_decorator(col, value, joins):
101
102 def decorator(q):
103 if joins:
104 q = q.join(joins, aliased=True)
105 return q.filter(col==value)
106
107 return decorator
108
109 options = [(self._value_to_string(value[0]), create_decorator(col, value[0], joins))
110 for value in session.execute(query)]
111
112 return (filter_names[0],[(_('all'), lambda q: q)] + options)
113
115 """Filter where the items are displayed in a QGroupBox"""
116
117 @gui_function
118 - def render(self, parent, name, options):
119
120 from PyQt4 import QtCore, QtGui
121 from camelot.view.controls.filterlist import filter_changed_signal
122
123 class FilterWidget(QtGui.QGroupBox):
124 """A box containing a filter that can be applied on a table view, this filter is
125 based on the distinct values in a certain column"""
126
127 def __init__(self, name, choices, parent):
128 QtGui.QGroupBox.__init__(self, unicode(name), parent)
129 self.group = QtGui.QButtonGroup(self)
130 self.item = name
131 self.unique_values = []
132 self.choices = None
133 self.setChoices(choices)
134
135 def emit_filter_changed(self, state):
136 self.emit(filter_changed_signal)
137
138 def setChoices(self, choices):
139 self.choices = choices
140 layout = QtGui.QVBoxLayout()
141 for i,name in enumerate([unicode(c[0]) for c in choices]):
142 button = QtGui.QRadioButton(name, self)
143 layout.addWidget(button)
144 self.group.addButton(button, i)
145 if i==0:
146 button.setChecked(True)
147 self.connect(button, QtCore.SIGNAL('toggled(bool)'), self.emit_filter_changed)
148 layout.addStretch()
149 self.setLayout(layout)
150
151 def decorate_query(self, query):
152 checked = self.group.checkedId()
153 if checked>=0:
154 return self.choices[checked][1](query)
155 return query
156
157 return FilterWidget(name, options, parent)
158
160 """Filter where the items are displayed in a QComboBox"""
161
162 @gui_function
163 - def render(self, parent, name, options):
164
165 from PyQt4 import QtCore, QtGui
166 from camelot.view.controls.filterlist import filter_changed_signal
167
168 class FilterWidget(QtGui.QGroupBox):
169
170 def __init__(self, name, choices, parent):
171 QtGui.QGroupBox.__init__(self, unicode(name), parent)
172 layout = QtGui.QVBoxLayout()
173 self.choices = choices
174 combobox = QtGui.QComboBox(self)
175 for i,(name,decorator) in enumerate(choices):
176 combobox.insertItem(i, unicode(name), QtCore.QVariant(decorator))
177 layout.addWidget(combobox)
178 self.setLayout(layout)
179 self.current_index = 0
180 self.connect(combobox, QtCore.SIGNAL('currentIndexChanged(int)'), self.emit_filter_changed)
181
182 def emit_filter_changed(self, index):
183 self.current_index = index
184 self.emit(filter_changed_signal)
185
186 def decorate_query(self, query):
187 if self.current_index>=0:
188 return self.choices[self.current_index][1](query)
189 return query
190
191 return FilterWidget(name, options, parent)
192
194 """Filter that presents the user with an editor, allowing the user to enter
195 a value on which to filter, and at the same time to show 'All' or 'None'
196 """
197
198 - def __init__(self, field_name, verbose_name=None):
199 """:param field: the name of the field on which to filter"""
200 super(EditorFilter, self).__init__(field_name)
201 self._field_name = field_name
202 self._verbose_name = verbose_name
203
204 - def render(self, parent, name, options):
208
213
215 """Filters entities that are valid a certain date. This filter will present
216 a date to the user and filter the entities that have their from date before this
217 date and their end date after this date. If no date is given, all entities will
218 be shown"""
219
220 - def __init__(self, from_attribute='from_date', thru_attribute='thru_date', verbose_name=_('Valid at')):
221 """
222 :param from_attribute: the name of the attribute representing the from date
223 :param thru_attribute: the name of the attribute representing the thru date
224 :param verbose_name: the displayed name of the filter"""
225 self._from_attribute = from_attribute
226 self._thru_attribute = thru_attribute
227 self._verbose_name = verbose_name
228
229 - def render(self, parent, name, options):
230
231 from datetime import date
232 from PyQt4 import QtGui, QtCore
233 from camelot.view.controls.filterlist import filter_changed_signal
234 from camelot.view.controls.editors import DateEditor, editingFinished
235
236 class FilterWidget(QtGui.QGroupBox):
237
238 def __init__(self, name, query_decorator, parent):
239 QtGui.QGroupBox.__init__(self, unicode(name), parent)
240 layout = QtGui.QVBoxLayout()
241 self.date_editor = DateEditor(parent=self, nullable=True)
242 self.date_editor.set_value(date.today())
243 self.query_decorator = query_decorator
244 layout.addWidget(self.date_editor)
245 self.setLayout(layout)
246 self.connect(self.date_editor, editingFinished, self.emit_filter_changed)
247
248 def emit_filter_changed(self):
249 self.emit(filter_changed_signal)
250
251 def decorate_query(self, query):
252 return self.query_decorator(query, self.date_editor.get_value())
253
254 return FilterWidget(name, options, parent)
255
257 from sqlalchemy.sql import and_
258
259 def query_decorator(query, date):
260 e = admin.entity
261 if date:
262 return query.filter(and_(getattr(e, self._from_attribute)<=date,
263 getattr(e, self._thru_attribute)>=date))
264 return query
265
266 return (self._verbose_name, query_decorator)
267