ajax_pattern_view.py 11.9 KB
# -*- coding: utf-8 -*-
from collections import OrderedDict

from django.contrib.auth.models import AnonymousUser
from django.core.urlresolvers import reverse
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.utils.http import urlencode
from django.utils.translation import ugettext as _

from common.decorators import render, ajax, AjaxError, render_ajax
from common.util import error_messages, format_date, json_encode
from dictionary.ajax_lexeme_view import refresh_derivatives
from dictionary.ajax_slickgrid import SlickGridQuery
from dictionary.models import Pattern, Ending, BaseFormLabel, PatternType, \
    editable_qualifiers, readonly_vocabularies, Gender, PatternExample, filter_visible_lips, Lexeme, reader_lexemes, \
    reader_patterns
from patterns.models import InflectionType, BaseFormLabel
from dictionary.util import check_query_params
from patterns.forms import PatternEditForm, QualifierForm

COLOR_SCHEMES = dict(InflectionType.objects.values_list('symbol', 'color_scheme'))


class PatternQuery(SlickGridQuery):
    model = Pattern
    sort_field = 'name'
    select_related = ['type__lexical_class']
    default_columns = ('name', 'type')
    column_data = {
        'id': lambda pattern: pattern.id,
        'scheme': lambda pattern: COLOR_SCHEMES[pattern.type.lexical_class_id],
        'name': lambda pattern: pattern.name,
        'type': lambda pattern: pattern.type.symbol,
        'lexical_class': lambda pattern: pattern.type.lexical_class_id,
        'example':
            lambda pattern: pattern.example + u'·' + pattern.basic_form_ending,
    }

    literal_filter_fields = ['name', 'old_name', 'type']

    filter_field_translation = {
        'lexical_class': 'type__lexical_class__symbol',
        'base_form_label': 'endings__base_form_label__symbol',
    }

    def __init__(self, *args, **kwargs):
        super(PatternQuery, self).__init__(*args, **kwargs)
        self.reader = self.query_params['reader']

    def get_queryset(self):
        patterns = super(PatternQuery, self).get_queryset()
        if self.reader:
            return reader_patterns(patterns)
        return patterns


# Zapytanie o indeks wiersza o pewnym id przy danym sortowaniu
@ajax(login_required=False, method='get')
def row_index(request, id, query_params):
    check_query_params(request, query_params)
    query = PatternQuery(query_params)
    return {'index': query.row_index(id)}


# Zapytanie o id oraz indeks wiersza przy danym sortowaniu
# pierwszego wiersza, którego hasło rozpoczyna się od search.
# 'selected_id' < 0, jeśli takiego nie ma
@ajax(login_required=False, method='get')
def search_index(request, query_params, search=''):
    check_query_params(request, query_params)
    query = PatternQuery(query_params)
    return {'index': query.search_index(search)}


@ajax(login_required=False, method='get')
def get_patterns(request, from_page, to_page, rows, query_params, columns):
    check_query_params(request, query_params)
    session_prefix = 'reader_' if query_params['reader'] else ''
    request.session[session_prefix + 'pattern-sort_rules'] = \
        query_params['sort_rules']
    request.session[session_prefix + 'pattern-filter'] = query_params['filter']
    request.session[session_prefix + 'pattern-columns'] = columns
    query = PatternQuery(query_params, columns=columns)
    rows, count = query.get_page(from_page, to_page, rows)
    return {
        'rows': rows,
        'count': count,
        'page': from_page,
    }


@render_ajax(template='pattern_preview.html', login_required=False,
             method='get')
def pattern_preview(request, id, reader=False):
    try:
        pattern = Pattern.all_objects.get(id=id)
    except Pattern.DoesNotExist:
        raise AjaxError(_(u'Pattern doesn\'t exist'))
    if reader:
        user = AnonymousUser()
    else:
        user = request.user
    if not request.user.is_authenticated() and not pattern.is_public():
        raise AjaxError('access denied')
    lips = filter_visible_lips(pattern.lexemeinflectionpattern_set, user)
    detailed_counts = []
    pattern_filter_rule = {
        'field': 'pattern_name',
        'op': 'eq',
        'data': pattern.name
    }

    def make_filter_url(rules):
        lexeme_url = reverse('reader_view' if reader else 'lexeme_view')
        return lexeme_url + '?' + urlencode(
            {'filter': json_encode({'group_op': 'AND', 'rules': rules})})

    def lexeme_count(q):
        return q.values('lexeme').distinct().count()

    if pattern.type.lexical_class_id == 'subst':
        for gender in Gender.objects.all():
            gender_count = lexeme_count(lips.filter(gender=gender))
            if gender_count > 0:
                example = pattern.get_example(gender)
                if example:
                    example_qs = Lexeme.objects.filter(id=example[0].id)
                    if not reader_lexemes(example_qs):
                        example = pattern.get_example(gender, refresh=True)
                    filter_url = make_filter_url([
                        pattern_filter_rule,
                        {
                            'field': 'gender',
                            'op': 'in',
                            'data': [str(gender.id)],
                        }
                    ])
                else:
                    filter_url = None
                detailed_counts.append(
                    (gender.symbol, gender_count, example, filter_url))
    if detailed_counts and detailed_counts[0][2]:
        first_root = detailed_counts[0][2][1]
    else:
        first_root = pattern.example
    return {
        'pattern': pattern,
        'root': first_root,
        'lexeme_count': lexeme_count(lips),
        'detailed_counts': detailed_counts,
        'pattern_filter_url': make_filter_url([pattern_filter_rule]),
    }


@render_ajax(template='pattern_edit_form.html', method='get',
             permission_required='dictionary.view_pattern')
def pattern_edit_form(request, id):
    p = Pattern.all_objects.get(pk=id)
    editable = request.user.has_perm('dictionary.change_pattern')
    ending_groups = OrderedDict(
        (bfl, []) for bfl in p.type.base_form_labels.all())
    endings = p.endings.order_by('index')
    for e in endings:
        ro_qualifers = e.qualifiers.filter(
            vocabulary__in=readonly_vocabularies(request.user))
        q_form = QualifierForm(
            qualified=e, user=request.user, editable=editable,
            prefix='end%s' % e.pk)
        assert e.base_form_label in ending_groups
        ending_groups[e.base_form_label].append((e, ro_qualifers, q_form))
    # examples = p.patternexample_set.all()
    return {
        'pattern': p,
        'form': PatternEditForm(instance=p, editable=editable),
        'editable': editable,
        'ending_groups': ending_groups,
    }


# mogłoby iść przez js_vars...
@render('ending_row.html')
@ajax(method='get', encode_result=False)
def new_ending_row(request, pattern_id):
    p = Pattern.all_objects.get(id=pattern_id)
    ending = {'string': '', 'id': 'add-NUM'}
    form = QualifierForm(user=request.user, prefix='add-NUM')
    return {'ending': ending, 'editable': True, 'form': form, 'pattern': p}


@render_ajax(template='ending_table_row.html', method='get')
def new_ending_table_row(request, bfl_id):
    return {
        'base_form_label': BaseFormLabel.objects.get(id=bfl_id),
        'ending_groups': [],
        'editable': True,
    }


@ajax(method='post', permission_required='dictionary.change_pattern')
def update_pattern(request, form_data):
    form_dict = dict((x['name'], x['value']) for x in form_data)
    p = Pattern.all_objects.get(pk=form_dict['id'])
    lc = p.type.lexical_class
    form = PatternEditForm(data=form_dict, instance=p)
    endings_changed = False
    if form.is_valid():
        form.save()
        p.deleted = False
        p.save()
        for ending_pk in form_dict['deleted']:
            Ending.objects.get(pk=int(ending_pk)).delete()
            endings_changed = True
        qualifiers = editable_qualifiers(request.user)
        for bfl_endings in form_dict['ending_list']:
            endings_data = bfl_endings['endings']
            bfl = BaseFormLabel.objects.get(
                symbol=bfl_endings['base_form_label'], lexical_class=lc)
            for index, ending_data in enumerate(endings_data, 1):
                quals = set(int(q) for q in ending_data['qualifiers'])
                if ending_data['id'] == 'add':
                    ending = Ending.objects.create(
                        pattern=p, base_form_label=bfl,
                        string=ending_data['string'],
                        index=index)
                    endings_changed = True
                else:
                    ending = Ending.objects.get(pk=int(ending_data['id']))
                    if ending.index != index \
                            or ending.string != ending_data['string']:
                        ending.index = index
                        ending.string = ending_data['string']
                        ending.save()
                        endings_changed = True
                for qualifier in qualifiers:
                    changed = qualifier.set_for(ending, qualifier.pk in quals)
                    if changed:
                        endings_changed = True
        for example_id, example_entry in form_dict['example_list']:
            example = PatternExample.objects.get(id=example_id)
            if example.lexeme.entry != example_entry:
                lexemes = reader_lexemes(Lexeme.objects.distinct().filter(
                    patterns=p, entry=example_entry))
                if len(lexemes) == 0:
                    raise AjaxError(
                        _(u'No matching lexeme: %s') % example_entry)
                if len(lexemes) > 1:
                    raise AjaxError(
                        _(u'%s has homonyms, choose a less ambiguous example') %
                        example_entry)
                example.lexeme = lexemes.get()
                example.save()
        if endings_changed:
            lexemes = p.lexeme_set.all()
            if len(lexemes) <= 100:
                for l in lexemes:
                    l.refresh_data()
            if p.type.lexical_class.symbol == 'v':
                for l in p.lexeme_set.all():
                    refresh_derivatives(l, request)
    else:
        raise AjaxError(error_messages(form))
    return {}


@ajax(method='post', permission_required='dictionary.change_pattern')
def create_pattern(request):
    new_name = u'nowy wzór %s'
    i = 1
    while Pattern.all_objects.filter(name=new_name % i).exists():
        i += 1
    pt = PatternType.objects.get(lexical_class_id='subst', symbol='m')
    p = Pattern.objects.create(
        name=new_name % i, type=pt, example='...', status=Pattern.STATUS_NEW,
        deleted=True)
    return {'id': p.id}


@ajax(method='get', login_required=False)
def get_name(request, pattern_id):
    pattern = get_object_or_404(Pattern, id=pattern_id)
    return {'name': pattern.name}


@ajax(method='post', permission_required='dictionary.change_pattern')
def clone_pattern(request, pattern_id):
    try:
        pattern = Pattern.objects.get(id=pattern_id)
    except Pattern.DoesNotExist:
        raise AjaxError(_(u'Cannot clone a pattern before it is created.'))
    comment = u'Wzór sklonowany %s' % (format_date(timezone.now()))
    i = 1
    while Pattern.all_objects.filter(name='%s-%s' % (pattern.name, i)).exists():
        i += 1
    new_name = '%s-%s' % (pattern.name, i)
    new_pattern = Pattern.objects.create(
        name=new_name, type=pattern.type, example=pattern.example,
        basic_form_ending=pattern.basic_form_ending, status=Pattern.STATUS_NEW,
        comment=comment, deleted=False)
    for ending in pattern.endings.all():
        new_ending = Ending.objects.create(
            pattern=new_pattern, index=ending.index,
            base_form_label=ending.base_form_label, string=ending.string)
        new_ending.qualifiers = ending.qualifiers.all()
    return {'new_id': new_pattern.id}