aboutsummaryrefslogtreecommitdiff
blob: 26b116c68fdf15df4ebee1c1240b2b3eb45f8f44 (plain)
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
from django.views.generic import TemplateView, ListView
from string import Template
import re
from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Atom1Feed, rfc3339_date
from django.utils import tzinfo
from django.utils.timezone import is_naive
from django.http import Http404

class ContextView(object):
    "Mixin to add additional data to context"
    extra_context = {}
    def get_context_data(self, **kwargs):
        ret = super(ContextView, self).get_context_data(**kwargs)
        ret.update(self.extra_context)
        return ret

class ContextListView(ContextView, ListView):
    pass

class ContextTemplateView(ContextView, TemplateView):
    pass

class SetLang(ContextTemplateView):
    "View for changing language js is unavailable"
    template_name = 'setlang.html'
    extra_context = {'page_name': 'Set Language', 'nav_name' : 'setlanguage'}

set_lang_view = SetLang.as_view()

# there is another dynamic filter for django, and it maybe better 
# but it is too big and i need just a litle of its functionly
# but if this code have to be grove maybe i replace it to django-filter
# application or another.
def dynamic_filter(filter_set, allowed, many_set = {}):
    result = {}
    for k, v in allowed.iteritems():
        if k in filter_set:
            vv = filter_set[k]
            if k in many_set and isinstance(vv, list):
                v += '__in'
            result[v] = vv
    return result

def cut_to_len(lst, num):
    if not num:
        return lst
    return lst[:num]

def filter_req(filter_set, allowed):
    result = {}
    for k in allowed.iterkeys():
        v = filter_set.get(k)
        if v:
            result[k] = v
    return result

def dynamic_order(order_attr, allowed_list, reverse = None):
    order = allowed_list.get(None)
    if order_attr in allowed_list:
        order = allowed_list.get(order_attr)

    if order == '?':
        return order
    
    if reverse and order[0] != '-':
        order = '-' + order
    elif reverse:
        order =  order[1:]
    return order

class MultipleFilterListViewMixin(object):
    allowed_filter = {}
    allowed_order = {}
    boolean_filters = ()
    # allowed_many = {'name': int_count}
    allowed_many = {}
    m2m_filter = set()

    def get_context_data(self, **kwargs):
        cd = super(MultipleFilterListViewMixin, self).get_context_data(**kwargs)
        cd['filters_dict'] = self.queries_dict
        return cd

    def get_base_filters(self):
        qs = filter_req(self.request.GET, self.allowed_filter)
        qs.update(filter_req(self.kwargs, self.allowed_filter))
        return qs

    def get_filters(self):
        qs = self.get_base_filters()
        newqs = {}
        for k, v in qs.iteritems():
            if k in self.allowed_many:
                vm = v.split(',')
                if len(vm)>1:
                    v = cut_to_len(vm, self.allowed_many[k])
            elif k in self.boolean_filters:
                v = True if v == 'yes' else False
            newqs[k] = v 
        self.queries_dict = newqs
        return newqs

    def is_reverse(self):
        if self.kwargs.get('rev') is None:
            reverse = bool(self.request.GET.get('rev', False))
        else:
            reverse = bool(self.kwargs.get('rev', False))

        return reverse

    def get_order(self):
        reverse = self.is_reverse()
        
        if 'order' in self.request.GET:
            order_attr =  self.request.GET.get('order')
        else:
            order_attr = self.kwargs.get('order')

        if order_attr not in self.allowed_order:
            raise Http404('no such order')
        order = dynamic_order(order_attr, self.allowed_order, reverse)
        return order

    def get_queryset(self):
        query = super(MultipleFilterListViewMixin, self).get_queryset()
        qs = self.get_filters()
        order = self.get_order()

        qa = dynamic_filter(qs, self.allowed_filter, self.allowed_many)
        queryset = query.filter(**qa).order_by(order)

        for q in qs.iterkeys():
            if q in self.m2m_filter:
                queryset = queryset.distinct()
                break

        return queryset

    @classmethod
    def get_url_part(cls):
        t = "(?:{0}/(?P<{0}>[^/]+)/)?"
        t_bool = "(?:{0}/(?P<{0}>yes|no)/)?"
        l =[]
        for key in cls.allowed_filter.iterkeys():
            if key in cls.boolean_filters:
                l.append(t_bool.format(re.escape(key)))
            else:
                l.append(t.format(re.escape(key)))

        # Maybe add num chars to order attribute ?
        return ''.join(l) + "(?:order/(?P<order>[a-z]*)/)?(?P<rev>rev/)?"

class FeedWithUpdated(Feed):
    def item_extra_kwargs(self, item):
        # for future
        kwargs = super(FeedWithUpdated, self).item_extra_kwargs(item)
        # hack for access to private method !!!
        updated = self._Feed__get_dynamic_attr('item_update', item)
        if updated and is_naive(updated):
            ltz = tzinfo.LocalTimezone(updated)
            updated = updated.replace(tzinfo=ltz)
        kwargs['updated'] = updated
        return kwargs
            
# see bug https://code.djangoproject.com/ticket/14656
class RightAtom1Feed(Atom1Feed):
    def add_item_elements(self, handler, item):
        if item['pubdate'] is not None:
            handler.addQuickElement(u"published", 
                rfc3339_date(item['pubdate']).decode('utf-8'))

            item['pubdate'] = None

        if item['updated'] is not None:
            handler.addQuickElement(u"updated", 
                rfc3339_date(item['updated']).decode('utf-8'))

        return super(RightAtom1Feed, self).add_item_elements(handler, item)