~dylanmccall/harvest/gsoc-client-stuff

« back to all changes in this revision

Viewing changes to harvest/common/filters_base.py

  • Committer: Dylan McCall
  • Date: 2010-06-11 03:06:36 UTC
  • Revision ID: dylanmccall@gmail.com-20100611030636-uapdngjaby6iuodj
First test of filter URLs. Any filter inside a group that does enabling / disabling can get a URL that toggles it.

New comments in filters_base.py identify which methods should be considered final, and which ones should be considered abstract.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
All This Does is filter things.
23
23
"""
24
24
 
 
25
#FIXME: FilterGroup and ChoiceFilter have TONS of duplication. Combine these somehow and remove the redundancy!
 
26
#TODO: check over strings that should be unicode, make sure they are, mention in docstrings
 
27
#TODO: function to add filters to select by default in FilterGroup
 
28
 
 
29
from copy import copy
25
30
from django.utils.safestring import mark_safe
26
31
 
27
 
#TODO: docstrings must specify that we need to convert lots of stuff to unicode strings
28
 
 
29
32
class Filter(object): #abstract
30
33
    id_str = ""
31
34
    parent = None #immediate parent FilterContainer (relative to this object)
32
35
    
33
 
    def __init__(self, id_str):
 
36
    def __init__(self, id_str): #final
34
37
        self.set_id(id_str)
35
38
        self.initialize()
36
39
    
37
 
    def initialize(self):
 
40
    def initialize(self): #abstract
38
41
        """
39
42
        Extend this function to do initialization stuff for an object
40
43
        without interfering with the functionality of __init__.
41
44
        """
42
45
        pass
43
46
    
44
 
    def set_id(self, id_str):
 
47
    def set_id(self, id_str): #final
45
48
        self.id_str = id_str
46
49
    
47
 
    def get_id(self):
 
50
    def get_id(self): #final
48
51
        return self.id_str
49
52
    
50
 
    def set_parent(self, parent):
 
53
    def set_parent(self, parent): #final
51
54
        self.parent = parent
52
55
    
53
 
    def get_parent(self):
 
56
    def get_parent(self): #final
54
57
        return self.parent
55
58
    
56
 
    def get_system(self):
 
59
    def get_system(self): #final
57
60
        parent = self.get_parent()
58
61
        system = None
59
62
        if isinstance(parent, Filter): 
62
65
            system = parent
63
66
        return system
64
67
    
65
 
    def get_full_name(self):
 
68
    def get_full_name(self): #final
66
69
        """
67
70
        Returns a unique name for the filter that makes sense globally.
68
71
        """
72
75
            full_name = "%s:%s" % (parent.get_id(), full_name)
73
76
        return full_name
74
77
    
75
 
    def set_value(self, value):
 
78
    def set_value(self, value): #abstract
76
79
        """
77
80
        Extend this to take a value passed down from the top, probably
78
81
        from FilterSystem.update_http, and do something with it.
81
84
        """
82
85
        pass
83
86
    
84
 
    def get_value(self):
85
 
        """
86
 
        Extend this to returnthe value of this filter in the same format
87
 
        it comes in. Used to generate URLs.
 
87
    def get_value(self): #abstract
 
88
        """
 
89
        @return: the value of this instance, in a format native to its type 
 
90
        """
 
91
        pass
 
92
    
 
93
    def get_value_string(self, value = None): #abstract
 
94
        """
 
95
        Extend this to return the value of this filter in the same
 
96
        format it comes in. Used to generate URLs.
 
97
        @param choices_dict: Optional value to be used instead of internal value
88
98
        @return: a unicode string formatted for a URL's GET parameters
89
99
        """
 
100
        #For now, when extending, you must include the line:
 
101
        #if not value: value = self.get_value()
90
102
        pass
91
103
    
92
 
    def process_queryset(self, queryset):
 
104
    def get_href(self):
 
105
        """
 
106
        @return: the url that toggles this filter's state
 
107
        """
 
108
        href = ""
 
109
        parent = self.get_parent()
 
110
        if isinstance(self.get_parent(), FilterGroup):
 
111
            href = parent.get_toggle_url(self.get_id())
 
112
        print("get_toggle_url: %s" % href)
 
113
        return href
 
114
    
 
115
    def process_queryset(self, queryset): #abstract
93
116
        """
94
117
        Extend this to manipulate a given queryset and then return it.
95
118
        For example, queryset.filter(name__startswith = self.value)
98
121
        """
99
122
        return queryset.all()
100
123
    
101
 
    def render(self):
 
124
    def render(self): #final
 
125
        """
 
126
        @param context: a django.template.Context object. Expected fields are in module documentation.
 
127
        @return: the default rendering of the filter itself in given context
 
128
        """
102
129
        return self.render_html()
103
130
    
104
 
    def render_html(self):
 
131
    def render_html(self): #abstract
105
132
        """
106
133
        Extend this to return the html output for the filter itself.
107
134
        The output should be very simple and semantically meaningful,
108
135
        with no specific concern about formatting. It will be
109
 
        placed within other tags that describe its context, and it is
 
136
        placed within other  tags that describe its context, and it is
110
137
        up to the template to describe style.
 
138
        @param context: a django.template.Context object. Expected fields are in module documentation.
111
139
        @return: a unicode string containing html representing this filter
112
140
        """
113
 
        return mark_safe(u'%s' % self.get_id())
 
141
        #Would be nice to use django.template.Template here
 
142
        return mark_safe(u'<a href="%s">(%s)</a>'
 
143
            % (self.get_href(), self.get_id()))
114
144
 
115
145
class EditFilter(Filter): #abstract, extend in application
116
 
    value = ""
 
146
    input_str = ""
117
147
    
118
148
    def set_value(self, value):
119
 
        self.value = value
 
149
        self.input_str = value
120
150
    
121
151
    def get_value(self):
122
 
        return self.value
 
152
        return self.input_str
 
153
    
 
154
    def get_value_string(self, value = None):
 
155
        if not value: value = self.get_value()
 
156
        return self.input_str
123
157
 
124
158
class ChoiceFilter(Filter): #abstract, extend in application
125
159
    choices_dict = dict()
129
163
        self.selected_set = set([s for s in value.split(",") if s in self.choices_dict])
130
164
    
131
165
    def get_value(self):
132
 
        return ",".join(self.choices_dict)
 
166
        return self.selected_set
 
167
    
 
168
    def get_value_string(self, value = None):
 
169
        if not value: value = self.get_value()
 
170
        return ",".join(value)
133
171
    
134
172
    def choice_is_selected(self, choice):
135
173
        return choice in self.selected_set
141
179
            if self.choice_is_selected(c):
142
180
                c_render = "<em>%s</em>" % c_render
143
181
            choices += c_render
144
 
        return mark_safe(u'%s:<ul>%s</ul>' % (self.get_id(), choices))
 
182
        
 
183
        return mark_safe(u'<a href="%s">%s</a>:<ul>%s</ul>' % (self.get_href(), self.get_id(), choices))
145
184
 
146
185
class FirstLetterFilter(Filter): #abstract, extend in application
147
186
    pass
158
197
    
159
198
    filters_dict = dict() #to access children by id
160
199
    
161
 
    def set_filters(self, filters_set):
 
200
    def set_filters(self, filters_set): #final
162
201
        """
163
202
        Add a set of filters to be children of this one.
164
203
        @param filter_set: a set of Filter objects
168
207
            self.filters_dict[child.get_id()] = child
169
208
            child.set_parent(self)
170
209
    
171
 
    def get_filter(self, id_str):
 
210
    def get_filter(self, id_str): #final
172
211
        """
173
212
        Get a child of this object, based on its id
174
213
        @param id_str: a string that identifies the child
179
218
        else:
180
219
            return None
181
220
    
182
 
    def get(self, full_name):
 
221
    def get(self, full_name): #final
183
222
        """
184
223
        Finds a child based on its full name
185
224
        @param full_name: an object's full name in the format parent:child
203
242
    A collection of other filters. Children are enabled and disabled
204
243
    freely. Their do_queryset functions are all mixed together into one.
205
244
    """
206
 
    #TODO: function to add filters to select by default
207
245
    selected_set = set()
208
246
    
209
247
    def __init__(self, id_str, filters_set):
215
253
    
216
254
    def set_value(self, value):
217
255
        """
218
 
        Expects value to be a string, containing list of filters to
219
 
        enable (separated by ",")
 
256
        @param value: a string containing a comma separated list of filters to enable
220
257
        """
221
258
        self.selected_set = set([f for f in value.split(',') if f in self.filters_dict])
222
259
    
223
260
    def get_value(self):
224
 
        return ",".join(self.choices_dict)
 
261
        return self.selected_set
 
262
    
 
263
    def get_value_string(self, value = None):
 
264
        if not value: value = self.get_value()
 
265
        return ",".join(value)
 
266
    
 
267
    def render_html(self):
 
268
        internal = ""
 
269
        for f in self.filters_dict:
 
270
            f_render = self.filters_dict[f].render_html()
 
271
            if self.filter_is_selected(f):
 
272
                f_render = "<em>%s</em>" % f_render
 
273
            internal += "<li>%s</li>" % f_render
 
274
        
 
275
        return mark_safe("%s:<ul>%s</ul>" % (self.get_id(), internal))
225
276
    
226
277
    def process_queryset(self, queryset):
227
278
        """
228
279
        Manipulates a queryset using the currently selected filters.
229
 
        @param: queryset  the queryset to be filtered
 
280
        @param queryset:  the queryset to be filtered
230
281
        @return: a new queryset, filtered based on selected filters
231
282
        """
232
283
        for f in self.selected_set:
233
284
            queryset = self.filters_dict[f].process_queryset(queryset) #returns something like QuerySet.filter(blah)
234
285
        return queryset
235
286
    
236
 
    def render_html(self):
237
 
        internal = ""
238
 
        for f in self.filters_dict:
239
 
            f_render = self.filters_dict[f].render() #Eek! What should I pass?!
240
 
            if self.filter_is_selected(f):
241
 
                f_render = "<em>%s</em>" % f_render
242
 
            internal += "<li>%s</li>" % f_render
243
 
        return mark_safe(u'Filters:<ul>%s</ul>' % internal)
 
287
    def get_toggle_url(self, filter_id):
 
288
        """
 
289
        Returns the URL that should be used to toggle the given filter
 
290
        that is a member of this FilterGroup.
 
291
        """
 
292
        select = set(copy(self.selected_set))
 
293
        if filter_id in select:
 
294
            select.remove(filter_id)
 
295
        else:
 
296
            select.add(filter_id)
 
297
        
 
298
        value = self.get_value_string(select)
 
299
        url = self.get_system().url_with_value({self.get_full_name() : value})
 
300
        return url
244
301
 
245
302
 
246
303
class FilterSystem(FilterContainer): #abstract, extend in application
251
308
    This object contains helper functions to deal with http requests
252
309
    and for finding filters based on their full names.
253
310
    """
 
311
    request = None # current http request
254
312
    
255
 
    def __init__(self):
 
313
    def __init__(self): #final
256
314
        self.initialize()
257
315
    
258
 
    def initialize(self):
 
316
    def initialize(self): #abstract
259
317
        """
260
318
        Extend this method to create all the filters inside
261
319
        self.set_filters, and whatever else is appropriate.
262
320
        """
263
321
        pass
264
322
    
265
 
    def update_http(self, request):
266
 
        """
267
 
        Updates the state of all filters based on the HTTP request
268
 
        @param request: Current HttpRequest object
269
 
        """
 
323
    def update_http(self, request): #final
 
324
        """
 
325
        Updates the state of all filters based on the HTTP request.
 
326
        @param request: New HttpRequest object
 
327
        """
 
328
        self.request = request
270
329
        for key in request.GET:
271
330
            filter_object = self.get(key)
272
331
            if filter_object:
273
 
                filter_object.set_value(request.GET[key])
 
 
b'\\ No newline at end of file'
 
332
                filter_object.set_value(request.GET[key])
 
333
    
 
334
    def url_with_value(self, add_dict): #final
 
335
        """
 
336
        Returns the URL with a list of variables added or changed,
 
337
        without affecting other variables or adding redundant entries.
 
338
        Derived from <http://djangosnippets.org/snippets/1627/>
 
339
        """
 
340
        get = self.request.GET.copy()
 
341
 
 
342
        for key in add_dict:
 
343
            get[key] = add_dict[key]
 
344
        
 
345
        path = self.request.META['PATH_INFO']
 
346
        
 
347
        #print "&".join(["%s=%s" % (key, value) for (key, value) in get.items() if value])
 
348
        
 
349
        if len(get):
 
350
            path += "?%s" % "&".join(["%s=%s" % (key, value) for (key, value) in get.items() if value])
 
351
        
 
352
        return path