forms.py 14.5 KB
# -*- coding: utf-8 -*-

from django.core.exceptions import ValidationError
from django.forms import Textarea, TextInput, ModelMultipleChoiceField, \
    ModelChoiceField, ModelForm, Form, CharField, SelectMultiple, Select, \
    TypedMultipleChoiceField, HiddenInput, IntegerField, ChoiceField

from common.forms import disable_field
from common.util import GroupDict
from dictionary.models import Lexeme, LexemeInflectionPattern, Pattern, \
    Vocabulary, CrossReference, Qualifier, editable_vocabularies, \
    editable_qualifiers, LexemeAttributeValue


# ze StackOverflow, http://stackoverflow.com/questions/3737116/
class QualifiersField(ModelMultipleChoiceField):
    def __init__(self, **kwargs):
        super(QualifiersField, self).__init__(
            Qualifier.objects.none(), **kwargs)
        self.to_field_name = None  # ?

    def set_qualifiers(self, user, qualified=None, types=None):
        # if qualified and qualified.id:
        #     if lexeme:
        #         vocabularies = lexeme.editable_vocabularies(user)
        #     else:
        #         vocabularies = qualified.editable_vocabularies(user)
        #     qualifiers = Qualifier.objects.filter(vocabulary__in=vocabularies)
        # else:
        qualifiers = editable_qualifiers(user)
        if types:
            qualifiers = qualifiers.filter(type__in=types)
        if not qualifiers:
            disable_field(self)
            return
        self.queryset = queryset = qualifiers.order_by('vocabulary', 'label')
        queryset = queryset.select_related('vocabulary')
        if qualified and qualified.pk:
            initial_qualifiers = qualified.qualifiers.all() & qualifiers
            self.initial = initial_qualifiers

        q_lists = GroupDict()
        for qualifier in queryset:
            q_lists.add(
                qualifier.vocabulary.id, (qualifier.id, qualifier.label))
        self.choices = sorted(q_lists.iteritems())


class QualifierField(ModelChoiceField):
    def __init__(self, **kwargs):
        qualifiers = Qualifier.objects.all()
        super(QualifierField, self).__init__(qualifiers, **kwargs)
        self.set_qualifiers(qualifiers)

    def set_qualifiers(self, qualifiers):
        if not qualifiers:
            disable_field(self)
            return
        qualifiers = qualifiers.select_related('vocabulary').order_by(
            'vocabulary', 'label')

        q_lists = GroupDict()
        for qualifier in qualifiers:
            q_lists.add(
                qualifier.vocabulary.id, (qualifier.id, qualifier.label))
        self.choices = sorted(q_lists.iteritems())


class LexemeEditForm(ModelForm):
    vocabularies = ModelMultipleChoiceField(
        queryset=Vocabulary.objects.none(), required=False)
    style_qualifiers = QualifiersField(
        required=False, label='kwal styl.',
        widget=SelectMultiple(attrs={'class': 'qualifiers'}))
    scope_qualifiers = QualifiersField(
        required=False, label='kwal. zakr.',
        widget=SelectMultiple(attrs={'class': 'qualifiers'}))
    new_owner = ModelChoiceField(
        queryset=Vocabulary.objects.none(), required=False)

    def __init__(self, user, editable=True, **kwargs):
        super(LexemeEditForm, self).__init__(**kwargs)
        editable_vocabs = editable_vocabularies(user)
        instance = getattr(self, 'instance', None)
        other_editable_vocabs = editable_vocabs.exclude(
            pk=instance.owner_vocabulary.pk)
        self.fields['entry'].required = True
        self.fields['new_owner'].queryset = editable_vocabs
        self.fields['vocabularies'].queryset = other_editable_vocabs
        self.fields['vocabularies'].initial = instance.visible_vocabularies(
            user)
        owner_choices = editable_vocabs.values_list('id', 'id')
        self.fields['new_owner'].choices = owner_choices
        if instance and instance.id:
            self.fields['new_owner'].initial = instance.owner_vocabulary
        if other_editable_vocabs.count() == 0:
            disable_field(self.fields['vocabularies'])
        self.fields['style_qualifiers'].set_qualifiers(
            user, instance, types=[Qualifier.TYPE_STYLE])
        self.fields['scope_qualifiers'].set_qualifiers(
            user, instance, types=[Qualifier.TYPE_SCOPE])
        if not editable and instance and instance.id:
            for name, field in self.fields.iteritems():
                if not name.startswith('qualifiers') and name != 'vocabularies':
                    disable_field(field)

    def clean_entry(self):
        entry = self.cleaned_data['entry']
        if ' ' in entry:
            raise ValidationError(u'niedozwolona spacja')
        return entry

    def clean_comment(self):
        data = self.cleaned_data['comment']
        return data.replace('\r', '')

    class Meta:
        model = Lexeme
        fields = (
            'part_of_speech',
            'entry',
            'pronunciation',
            'valence',
            'status',
            'gloss',
            'note',
            'comment',
            'source',
            'borrowing_source',
            'qualifiers_dor',
            # 'qualifiers_style',
            # 'qualifiers_scope',
            'specialist',
            'extended_note',
        )
        widgets = {
            'comment': Textarea(attrs={'cols': 40, 'rows': 5}),
            'extended_note': Textarea(attrs={'cols': 40, 'rows': 5}),
            'gloss': TextInput(attrs={'size': 40}),
            'note': TextInput(attrs={'size': 40}),
            'pronunciation': TextInput(attrs={'size': 40}),
            'valence': TextInput(attrs={'size': 40}),
            'source': TextInput(attrs={'size': 40}),
            'qualifiers_dor': TextInput(attrs={'size': 40}),
            'qualifiers_style': TextInput(attrs={'size': 40}),
            'qualifiers_scope': TextInput(attrs={'size': 40}),
        }


# abstract
class LexemeAttributeForm(Form):
    def __init__(self, attribute, required=True, **kwargs):
        super(LexemeAttributeForm, self).__init__(**kwargs)
        self.fields['value'].label = attribute.name
        self.fields['value'].required = attribute.required and required


class LexemeOpenAttributeForm(LexemeAttributeForm):
    value = CharField(widget=TextInput(attrs={'size': 40}))

    def __init__(self, attribute, initial_value=None, **kwargs):
        assert not attribute.closed and not attribute.multiple
        super(LexemeOpenAttributeForm, self).__init__(attribute, **kwargs)
        if initial_value is not None:
            self.fields['value'].initial = initial_value.value


class LexemeClosedAttributeForm(LexemeAttributeForm):
    value = ModelChoiceField(
        queryset=LexemeAttributeValue.objects.none(),
        widget=Select(attrs={'class': 'attr-value-select'}))

    def __init__(self, attribute, initial_value=None, **kwargs):
        assert attribute.closed and not attribute.multiple
        super(LexemeClosedAttributeForm, self).__init__(attribute, **kwargs)
        self.fields['value'].queryset = attribute.values
        self.fields['value'].initial = initial_value


class LexemeMultipleAttributeForm(LexemeAttributeForm):
    value = ModelMultipleChoiceField(
        queryset=LexemeAttributeValue.objects.none())

    def __init__(self, attribute, initial_value=None, **kwargs):
        assert attribute.closed and attribute.multiple
        super(LexemeMultipleAttributeForm, self).__init__(attribute, **kwargs)
        self.fields['value'].queryset = attribute.values
        self.fields['value'].initial = initial_value


class LIPEditForm(ModelForm):
    pattern_name = CharField(widget=TextInput(
        attrs={'class': 'pattern', 'size': '10'}), label=u'Wzór')
    qualifiers = QualifiersField(
        required=False, label=u'Kwal.',
        widget=SelectMultiple(attrs={'class': 'lip-qualifiers'}))
    lexical_class = CharField(widget=HiddenInput(), label=u'')

    def __init__(self, part_of_speech, user, editable=True, index=None,
                 **kwargs):
        super(LIPEditForm, self).__init__(**kwargs)
        if part_of_speech.lexical_class_id != 'subst':
            self.fields['gender'].widget = HiddenInput()
            self.fields['gender'].label = ''
        instance = getattr(self, 'instance', None)
        self.fields['lexical_class'].initial = part_of_speech.lexical_class_id
        self.fields['qualifiers'].set_qualifiers(
            user, instance, types=[Qualifier.TYPE_STYLE, Qualifier.TYPE_FORM])
        if instance and instance.id:
            self.fields['pattern_name'].initial = instance.pattern.name
            if not editable:
                for name, field in self.fields.iteritems():
                    if name != 'qualifiers':
                        disable_field(field)
        self.index = index

    def clean(self):
        cleaned_data = self.cleaned_data
        if 'pattern_name' in cleaned_data:
            try:
                pattern = Pattern.objects.get(name=cleaned_data['pattern_name'])
            except Pattern.DoesNotExist:
                raise ValidationError(u'Niepoprawna nazwa wzoru.')
            lc = cleaned_data['lexical_class']
            if pattern.type.lexical_class_id != lc:
                raise ValidationError(u'Wzór nie pasuje do części mowy.')
            cleaned_data['pattern'] = pattern
        return cleaned_data

    def save(self, *args, **kwargs):
        lip = self.instance
        lip.pattern = self.cleaned_data['pattern']
        if self.index:
            lip.index = self.index
        super(LIPEditForm, self).save(*args, **kwargs)
        lip.save()
        return lip

    class Meta:
        model = LexemeInflectionPattern
        fields = ['gender']
        widgets = {'gender': Select(attrs={'class': 'gender'})}


class ClassificationForm(Form):
    values = TypedMultipleChoiceField(
        choices=[], empty_value=None, coerce=int,
        widget=SelectMultiple(attrs={'class': 'classification-values'}))

    def __init__(self, classification, editable=True, values=None,
                 required=True, **kwargs):
        super(ClassificationForm, self).__init__(**kwargs)
        self.fields['values'].label = classification.name
        self.fields['values'].choices = classification.make_choices()
        self.fields['values'].required = required
        if values:
            self.fields['values'].initial = [value.pk for value in values]
        if not editable:
            for _name, field in self.fields.iteritems():
                disable_field(field)


class CrossReferenceForm(ModelForm):
    entry = CharField(label=u'hasło', widget=TextInput(attrs={
        'class': 'entry',
        'size': 12,
    }))
    homonym_number = IntegerField(
        label=u'hom.',
        widget=TextInput(attrs={
            'class': 'homonym-number',
            'readonly': 'readonly',
            'size': 1,
        }))

    def __init__(self, lexeme=None, pos=None, **kwargs):
        super(CrossReferenceForm, self).__init__(**kwargs)
        if lexeme is not None:
            if pos is None:
                pos = lexeme.part_of_speech
            crtypes = pos.crtype_to.all()
            type_choices = crtypes.values_list('pk', 'desc')
            self.fields['type'].widget.choices = type_choices
            self.fields['from_lexeme'].initial = lexeme.pk

    def clean(self):
        cleaned_data = self.cleaned_data
        from_lexeme = cleaned_data.get('from_lexeme')
        entry = cleaned_data.get('entry')
        hom = cleaned_data.get('homonym_number')
        cr_type = cleaned_data.get('type')
        try:
            to_lexeme = Lexeme.objects.get(
                entry=entry, homonym_number=hom, part_of_speech=cr_type.to_pos)
        except Lexeme.DoesNotExist:
            raise ValidationError(u'Wybrany leksem nie istnieje.')
        except Lexeme.MultipleObjectsReturned:
            # jeśli się zdarzy, to jest niespójność w danych
            raise ValidationError(u'Niejednoznaczny numer homonimu!')
        if from_lexeme and to_lexeme and cr_type:
            if cr_type.from_pos != from_lexeme.part_of_speech:
                raise ValidationError(u'Nieprawidłowa część mowy w odsyłaczu.')
        cleaned_data['to_lexeme'] = to_lexeme
        return cleaned_data

    def save(self, commit=True):
        cr = super(CrossReferenceForm, self).save(False)
        cr.to_lexeme = self.cleaned_data['to_lexeme']
        if commit:
            cr.save()
        return cr

    class Meta:
        model = CrossReference
        fields = ['type', 'from_lexeme']
        widgets = {
            'type': Select(attrs={'class': 'type'}),
            'from_lexeme': HiddenInput(),
        }


# dynamic actions

ADD_REMOVE_CHOICES = (
    ('add', u'Dodaj'),
    ('remove', u'Usuń'),
)


class StatusActionForm(Form):
    action = ChoiceField(
        choices=[('set', u'ustaw')], label=u'',
        widget=Select(attrs={'class': 'action-choice'}))


class StatusValueForm(Form):
    value = ChoiceField(
        choices=Lexeme.STATUS_CHOICES, label=u'',
        widget=Select(attrs={'class': 'value-choice'}))

    def __init__(self, request, **kwargs):
        super(StatusValueForm, self).__init__(**kwargs)


class UsingVocabularyActionForm(Form):
    action = ChoiceField(
        choices=ADD_REMOVE_CHOICES, label=u'',
        widget=Select(attrs={'class': 'action-choice'}))


class VocabularyValueForm(Form):
    value = ModelChoiceField(
        queryset=Vocabulary.objects.none(), label=u'',
        widget=Select(attrs={'class': 'value-choice'}))

    def __init__(self, request, **kwargs):
        super(VocabularyValueForm, self).__init__(**kwargs)
        self.fields['value'].queryset = editable_vocabularies(request.user)


class LexemeQualifierActionForm(Form):
    action = ChoiceField(
        choices=ADD_REMOVE_CHOICES, label=u'',
        widget=Select(attrs={'class': 'action-choice'}))


class LexemeQualifierValueForm(Form):
    value = QualifierField(
        label=u'', widget=Select(attrs={'class': 'value-choice'}))

    def __init__(self, request, **kwargs):
        super(LexemeQualifierValueForm, self).__init__(**kwargs)
        self.fields['value'].set_qualifiers(editable_qualifiers(request.user))


ACTION_FIELDS = (
    (
        'status',
        (u'Status', StatusActionForm, StatusValueForm)
    ),
    (
        'using_vocabulary',
        (u'Słownik używający', UsingVocabularyActionForm, VocabularyValueForm)
    ),
    (
        'lexeme_qualifier',
        (
            u'Kwalifikator leksemu',
            LexemeQualifierActionForm, LexemeQualifierValueForm
        )
    ),
)


class ActionFieldForm(Form):
    field = ChoiceField(
        choices=[(id, data[0]) for id, data in ACTION_FIELDS], label=u'',
        widget=Select(attrs={'class': 'field-choice'}))