ajax_slickgrid.py 6.26 KB
# -*- coding: utf-8 -*-
from django.db.models import Min, Max
from dictionary.models import FreqLevel

class SlickGridQuery(object):
    model = None
    sort_field = None
    literal_filter_fields = []
    filter_field_translation = {}
    select_related = []
    default_columns = ()
    column_data = {}

    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',
        'in': 'in',
        '-in': '-in',
        'isnull': 'isnull',
        '-isnull': '-isnull',
        'belongs': 'in',
        '-belongs': '-in',
    }

    def __init__(self, query_params, columns=None):
        self.query_params = query_params
        self.filter = query_params['filter']
        self.sort_rules = query_params['sort_rules']
        if columns:
            self.columns = tuple(columns)
        else:
            self.columns = self.default_columns
        # tymczasowa łata
        self.columns = tuple(
            column for column in self.columns if column is not None)

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

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

    def translate_filter_field(self, field):
        if field in self.literal_filter_fields:
            return field
        elif field in self.filter_field_translation:
            return self.filter_field_translation[field]
        else:
            raise ValueError(u'Incorrect filter field: %s.' % field)

    def apply_filter_rule(self, queryset, rule):
        # Specjalna obsługa frekwencji
        if rule['field'] == 'freq_level':
            if rule['op'] == 'isnull':
                if rule['data']: # True dla "równy", False dla "różny od"
                    queryset = queryset.filter(freq__isnull = True)
                else:
                    queryset = queryset.filter(freq__isnull = False)
            else:
                negated = (rule['op'][0] == '-')
                lev = FreqLevel.objects.filter(id__in = rule['data']).aggregate(Min('level'))
                if lev['level__min'] == 0: # Brak poświadczeń
                    if negated:
                        queryset = queryset.filter(freq__gt = 0)
                    else:
                        queryset = queryset.filter(freq = 0)
                else:
                    if negated:
                        queryset = queryset.filter(freq__lt = lev['level__min'])
                    else:
                        queryset = queryset.filter(freq__gte = lev['level__min'])
        else:
            lookup = self.lookup_translation[rule['op']]
            negated = (lookup[0] == '-')
            lookup = lookup.lstrip('-')
            field = self.translate_filter_field(rule['field'])
            data = rule['data']
            if not data and lookup == 'in':
                lookup = 'isnull'
                data = not negated
                negated = False
            arg = {(field + '__' + lookup): data}
            try:
                if negated:
                    queryset = queryset.exclude(**arg).distinct()
                else:
                    queryset = queryset.filter(**arg).distinct()
            except ValueError:
                pass
        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 sort_queryset(self, queryset):
        order_list = [self.sort_field]
        return queryset.extra(order_by=order_list)

    def filter_from(self, queryset, from_value, upward):
        lookup = '__gte' if upward else '__lte'
        return queryset.filter(**{self.sort_field + lookup: from_value})

    # indeks wiersza w danym sortowaniu, w którym
    # znajdzie się rekord o danym id
    def row_index(self, record_id):
        selected = self.model.objects.get(pk=record_id)
        queryset = self.apply_filter()
        if selected not in queryset:
            return None
        data = getattr(selected, self.sort_field)
        preceding = self.filter_from(
            queryset, from_value=data, upward=False)
        return max(preceding.count() - 1, 0)

    def search_index(self, mask):
        queryset = self.apply_filter()
        count = queryset.count()
        if count > 0 and mask == '':
            return 0
        if queryset.count() > 0:
            whole_queryset = queryset
            queryset = self.filter_from(
                queryset, from_value=mask, upward=True)
            if queryset.count() == 0:
                queryset = whole_queryset
            queryset = self.sort_queryset(queryset)
            index = self.row_index(queryset[0].id)
        else:
            index = 0
        return index

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

    @staticmethod
    def count_pages(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, record):
        return dict((column, self.column_data[column](record))
                    for column in ('id', 'scheme', 'hidden') + self.columns)

    def prepare_rows(self, records):
        return [self.response_row(record) for record in records]

    def get_page(self, from_page, to_page, limit):
        queryset = self.get_sorted_queryset()
        if self.select_related:
            queryset = queryset.select_related(*self.select_related)
        count = queryset.count()
        start, response_rowcount = self.count_pages(from_page, to_page, limit)
        records = queryset[start:start + response_rowcount]
        return self.prepare_rows(records), count