ajax_slickgrid.py 6.01 KB
#-*- coding:utf-8 -*-

from accounts.models import filtering_mode

# TODO jedna klasa zamiast dwóch
class SlickGridQuery(object):
    model = None
    search_field = None
    sort_field_translation = {}
    filter_field_translation = {}

    lookup_translation = {
        'eq': 'exact',
        'ne': '-exact',
        'bw': 'startswith',
        'bn': '-startswith',
        'ew': 'endswith',
        'en': '-endswith',
        'cn': 'contains',
        'nc': '-contains',
        're': 'regex',
        'nr': '-regex',
        'le': 'lte',
        'ge': 'gte',
    }

    def __init__(self, filter, sort_rules, mask, user):
        self.filter = filter
        self.sort_rules = sort_rules
        self.mask = mask
        self.user = user

    def filtering_mode(self):
        return filtering_mode(self.user)

    def get_queryset(self):
        return self.model.objects.all()

    def get_empty_queryset(self):
        return self.model.objects.none()

    def apply_filter_rule(self, queryset, rule):
        lookup = self.lookup_translation[rule['op']]
        negated = (lookup[0] == '-')
        lookup = lookup.lstrip('-')
        field = self.filter_field_translation[rule['field']]
        data = rule['data']
        arg = {(field + '__' + lookup): data}
        if negated:
            queryset = queryset.exclude(**arg)
        else:
            queryset = queryset.filter(**arg).distinct()
        return queryset

    def apply_filter(self):
        queryset = self.get_queryset()
        if self.filter:
            if self.filter['group_op'] == 'AND':
                for rule in self.filter['rules']:
                    queryset = self.apply_filter_rule(queryset, rule)
            elif self.filter['group_op'] == 'OR':
                new_queryset = self.get_empty_queryset()
                for rule in self.filter['rules']:
                    new_queryset |= self.apply_filter_rule(queryset, rule)
                queryset = new_queryset
        return queryset

    def translate_sort_field(self, field):
        if field in self.sort_field_translation:
            return self.sort_field_translation[field]
        else:
            return field

    def get_sort_field(self, rule):
        field = self.translate_sort_field(rule['field'])
        if rule['order'] == 'desc':
            field = '-' + field
        return field

    def sort_queryset(self, queryset):
        order_list = []
        for rule in self.sort_rules:
            order_list.append(self.get_sort_field(rule))
        return queryset.extra(order_by=order_list)

    def apply_mask(self, queryset):
        pass # abstract

    # filtruje queryset według pola z reguły rule od wartości from,
    # wartości dalsze w porządku jeśli upward, w przeciwnym razie bliższe
    #
    # głupia nazwa
    def filter_value(self, queryset, rule, from_value, upward):
        greater = (rule['order'] == 'asc') == upward
        if greater:
            lookup = '__gte'
        else:
            lookup = '__lte'
        field = self.translate_sort_field(rule['field'])
        return queryset.filter(**{field + lookup: from_value})

    # id instancji z search_field rownym mask badz takiej, ktora bylaby nastepna
    # po instancji z search_field równym mask w danym sortowaniu.
    # Jezeli nie ma 'wiekszej' instancji badz reguly sortowania nie uwzgledniaja
    # search_field, metoda zwroci pierwsza instancje w danym sortowaniu
    #
    # beznadziejna nazwa metody...
    def get_pk(self):
        whole_queryset = self.apply_filter()
        queryset = whole_queryset
        rule = self.sort_rules[0]
        if rule['field'] == self.search_field:
            queryset = self.filter_value(
                queryset, rule, from_value=self.mask, upward=True)
            if queryset.count() == 0:
                queryset = whole_queryset
        queryset = self.sort_queryset(queryset)
        return queryset[0].pk

    def get_field(self, field, instance):
        return getattr(instance, field)

    # indeks wiersza w danym sortowaniu, w którym
    # znajdzie się instancja o danym id
    def row_index(self, record_id):
        selected = self.model.objects.get(pk=record_id)
        queryset = self.apply_filter()
        if self.filtering_mode():
            queryset = self.apply_mask(queryset)
        if queryset.count() == 0:
            return 0
        preceding = None
        assert len(self.sort_rules) > 0
        for rule in self.sort_rules:
            data = self.get_field(rule['field'], selected)
            preceding = self.filter_value(
                queryset, rule, from_value=data, upward=False)
        return max(preceding.count() - 1, 0)

    def search_index(self):
        queryset = self.apply_filter()
        count = queryset.count()
        if count > 0 and self.mask == '':
            return 0
        if self.filtering_mode():
            queryset = self.apply_mask(queryset)
        if queryset.count() > 0:
            selected_pk = self.get_pk()
            index = self.row_index(selected_pk)
        else:
            index = 0
        return index

    def get_sorted_queryset(self):
        queryset = self.apply_filter()
        if self.filtering_mode():
            queryset = self.apply_mask(queryset)
        return self.sort_queryset(queryset)

    def count_pages(self, from_page, to_page, limit):
        start = limit * from_page
        pages = to_page - from_page + 1
        response_rowcount = limit * pages
        return start, response_rowcount

    def response_row(self, instance):
        pass # abstract

    def make_response(self, response_qs, count, page):
        rows = [self.response_row(instance) for instance in response_qs]
        return {
            'page': page,
            'count': count,
            'rows': rows,
        }

    def get_page(self, from_page, to_page, limit):
        queryset = self.get_sorted_queryset()
        count = queryset.count()
        start, response_rowcount = self.count_pages(from_page, to_page, limit)
        response_qs = queryset[start:start + response_rowcount]
        return self.make_response(response_qs, count, from_page)