forms.py 13.9 KB
import re

from django.forms import ChoiceField, ModelChoiceField, ModelForm, Textarea, CharField, Form, formset_factory
from django import forms

from .models import Chunk, Document, Participant, Metadata, Keyword
from projects.ppc.models import Utterance
from collector import settings


class ChunkForm(ModelForm):

    def __init__(self, doc_id=None, pk=None, *args, **kwargs):
        super(ChunkForm, self).__init__(*args, **kwargs)
        self.document_id = doc_id
        self.old_sequence = self.instance.sequence
        if pk is not None:  # adding a new chunk
            self.prev_chunk = Chunk.objects.get(pk=pk)
            self.fields['sequence'].initial = self.prev_chunk.sequence + 1
        else:
            self.prev_chunk = None
            self.fields['sequence'].initial = 0

    def save(self, commit=True):
        if self.document_id:
            if self.prev_chunk is not None:
                next_chunks = Document.objects.get(id=self.document_id).chunks. \
                    filter(sequence__gt=self.prev_chunk.sequence)
            else:  # adding a chunk at the beginning of a list of chunks
                next_chunks = Document.objects.get(id=self.document_id).chunks.order_by('sequence')
            if next_chunks.first() is not None and next_chunks.first().sequence == self.cleaned_data['sequence']:
                for chunk in next_chunks:
                    chunk.sequence += 1
                    chunk.save()
            chunk, _ = Chunk.objects.get_or_create(document=Document.objects.get(id=self.document_id),
                                                   sequence=self.cleaned_data['sequence'],
                                                   text=self.cleaned_data['text'])
        else:
            new_chunk_seq = self.cleaned_data['sequence']
            try:
                doc = Document.objects.get(id=self.instance.document.id)
                doc.chunks.get(sequence=new_chunk_seq)  # if the new sequence is the same as other chunk's sequence
                if self.old_sequence > new_chunk_seq:
                    chunks_to_move = doc.chunks.filter(sequence__gte=new_chunk_seq,
                                               sequence__lt=self.old_sequence)
                    if chunks_to_move is not None:
                        for chunk in chunks_to_move:
                            chunk.sequence += 1
                            chunk.save()
                elif self.old_sequence < new_chunk_seq:
                    chunks_to_move = doc.chunks.filter(sequence__gt=self.old_sequence,
                                               sequence__lte=new_chunk_seq)
                    if chunks_to_move is not None:
                        for chunk in chunks_to_move:
                            chunk.sequence -= 1
                            chunk.save()
            except Chunk.DoesNotExist:
                pass
            chunk = super(ChunkForm, self).save(commit)

        chunk.document.changed = True
        chunk.document.save()

        return chunk

    class Meta:
        model = Chunk
        fields = ['sequence', 'text']
        labels = {
            'sequence': 'Pozycja',
            'text': 'Tekst'
        }


class SubchunkForm(ModelForm):

    def __init__(self, chunk_pk=None, *args, **kwargs):
        super(SubchunkForm, self).__init__(*args, **kwargs)
        self.chunk_pk = chunk_pk
        if chunk_pk:
            chunk = Chunk.objects.get(pk=chunk_pk)
            if chunk.utterances.exists():
                self.fields['sequence'].initial = chunk.utterances.last().sequence + 1
            else:
                self.fields['sequence'].initial = 0
            self.fields['speaker'].queryset = chunk.document.participants.filter(type='person')
        else:
            self.fields['speaker'].queryset = self.instance.chunk.document.participants.filter(type='person')

    def save(self, commit=True):
        if self.chunk_pk:
            utt, _ = Utterance.objects.get_or_create(chunk=Chunk.objects.get(pk=self.chunk_pk),
                                                     sequence=self.cleaned_data['sequence'],
                                                     text=self.cleaned_data['text'],
                                                     speaker=self.cleaned_data['speaker'])
        else:
            utt = super(SubchunkForm, self).save(commit)

        utt.chunk.document.changed = True
        utt.chunk.document.save()

        return utt

    class Meta:
        model = Utterance
        fields = ['sequence', 'speaker', 'text']
        labels = {
            'sequence': 'Pozycja',
            'speaker': 'Mówca',
            'text': 'Tekst'
        }


class ParticipantForm(ModelForm):

    def __init__(self, doc_id=None, *args, **kwargs):
        super(ParticipantForm, self).__init__(*args, **kwargs)
        self.document_id = doc_id

        if self.document_id:
            document = Document.objects.get(id=doc_id)
            if document.participants.exists():
                self.fields['order'].initial = document.participants.filter(type='person').last().order + 1
            else:
                self.fields['order'].initial = 0

        self.fields['type'].initial = 'person'
        self.fields['type'].disabled = True

        self.fields['abbrev'].widget = Textarea(attrs={'rows': 2})
        self.fields['name'].widget = Textarea(attrs={'rows': 2})

        roles_choices = [(role, role) for role in
                         Participant.objects.filter(type='person').order_by().values_list('role', flat=True).distinct()]
        self.fields['role'] = ChoiceField(choices=roles_choices)
        self.fields['role'].initial = 'speaker'

    def clean(self):
        cleaned_data = super(ParticipantForm, self).clean()
        xml_id_pattern = re.compile(r'^[a-zA-Z_][a-zA-Z_.\d-]*$')
        if 'abbrev' in cleaned_data and not xml_id_pattern.match(cleaned_data['abbrev']):
            self.add_error('abbrev', 'Identyfikator musi być zgodny ze standardem XML.')

    def save(self, commit=True):
        if self.document_id:
            participant, _ = Participant.objects.get_or_create(document=Document.objects.get(id=self.document_id),
                                                               abbrev=self.cleaned_data['abbrev'],
                                                               name=self.cleaned_data['name'],
                                                               order=self.cleaned_data['order'],
                                                               role=self.cleaned_data['role'],
                                                               type=self.cleaned_data['type'])
        else:
            participant = super(ParticipantForm, self).save(commit)

        participant.document.changed = True
        participant.document.save()

        return participant

    class Meta:
        model = Participant
        fields = ['order', 'abbrev', 'name', 'role', 'type']
        labels = {
            'order': 'Pozycja',
            'abbrev': 'Identyfikator',
            'name': 'Nazwa',
            'role': 'Rola',
            'type': 'Typ'
        }


class AuthorForm(ModelForm):

    def __init__(self, doc_id=None, *args, **kwargs):
        super(AuthorForm, self).__init__(*args, **kwargs)
        self.document_id = doc_id

        if self.document_id:
            document = Document.objects.get(id=doc_id)
            if document.participants.exists():
                self.fields['order'].initial = document.participants.all().last().order + 1
            else:
                self.fields['order'].initial = 0

        self.fields['name'].widget = Textarea(attrs={'rows': 1})
        roles_choices = (
            ('author', 'autor'),
            ('translator', 'tłumacz')
        )
        self.fields['role'] = ChoiceField(choices=roles_choices, label="Rola")

    def save(self, commit=True):
        if self.document_id:
            author, _ = Participant.objects.get_or_create(document=Document.objects.get(id=self.document_id),
                                                          order=self.cleaned_data['order'],
                                                          name=self.cleaned_data['name'],
                                                          gender=self.cleaned_data['gender'],
                                                          role=self.cleaned_data['role'],
                                                          type='person')
        else:
            author = super(AuthorForm, self).save(commit)

        author.document.changed = True
        author.document.save()

        return author

    class Meta:
        model = Participant
        fields = ['order', 'role', 'name', 'gender']
        labels = {
            'order': 'Pozycja',
            'name': 'Nazwa',
            'gender': 'Płeć'
        }


class MetadataForm(ModelForm):

    def __init__(self, doc_id=None, *args, **kwargs):
        super(MetadataForm, self).__init__(*args, **kwargs)
        self.document_id = doc_id
        self.fields['name'].attrs = {'class': 'autoComplete'}
        self.fields['value'].widget = Textarea(attrs={'rows': 1})
        if self.document_id is not None:
            document = Document.objects.get(pk=self.document_id)
            initial_sequence = self.get_first_available_sequence(document.metadata.all())
            self.fields['sequence'].initial = initial_sequence

    def save(self, commit=True):
        if self.document_id:
            metadata, _ = Metadata.objects.get_or_create(
                document=Document.objects.get(id=self.document_id),
                name=self.cleaned_data['name'],
                sequence=self.cleaned_data['sequence'],
                value=self.cleaned_data['value'])
        else:
            metadata = super(MetadataForm, self).save(commit)

        if self.cleaned_data['sequence'] is None:
            all_metadata = metadata.document.metadata.all()
            sequence = self.get_first_available_sequence(all_metadata)
            metadata.sequence = sequence
            metadata.save()

        metadata.document.changed = True
        metadata.document.save()

        return metadata

    def get_first_available_sequence(self, metadata):
        if metadata.count() > 1:
            sequences = list(metadata.values_list('sequence', flat=True).order_by('sequence'))
            sequences = [seq for seq in sequences if seq is not None]
            for seq in range(1, max(sequences) + 2):
                if seq not in sequences:
                    return seq
        else:  # first metadata of the document
            return 1

    class Meta:
        model = Metadata
        fields = ['sequence', 'name', 'value']
        labels = {
            'sequence': 'Kolejność',
            'name': 'Nazwa',
            'value': 'Wartość'
        }


class DocDetailsForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super(DocDetailsForm, self).__init__(*args, **kwargs)
        self.fields['title'].widget = Textarea(attrs={'rows': 1})

    def save(self, commit=True):
        document = super(DocDetailsForm, self).save(commit)
        document.changed = True
        document.save()
        return document

    class Meta:
        model = Document
        fields = ['title', 'publication_date', 'publication_place', 'number', 'original_lang']
        labels = {
            'title': 'Tytuł',
            'publication_date': 'Data publikacji',
            'publication_place': 'Miejsce publikacji',
            'number': 'Numer',
            'original_lang': 'Język oryginału'
        }


class SubDocDetailsForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super(SubDocDetailsForm, self).__init__(*args, **kwargs)
        self.fields['title'].widget = Textarea(attrs={'rows': 1})

    def save(self, commit=True):
        document = super(SubDocDetailsForm, self).save(commit)
        document.changed = True
        document.save()
        return document

    class Meta:
        model = Document
        fields = ['title', 'original_lang']
        labels = {
            'title': 'Tytuł',
            'original_lang': 'Język oryginału'
        }


class KeywordForm(Form):
    label = CharField(max_length=200, label='Etykieta')


class DocSplitForm(ModelForm):
    class ChunkChoiceField(ModelChoiceField):
        def label_from_instance(self, obj):
            return str(obj.sequence)

    chunk_beg = ChunkChoiceField(queryset=None, label='Początek poddokumentu')
    chunk_end = ChunkChoiceField(queryset=None, label='Koniec poddokumentu')

    def __init__(self, *args, **kwargs):
        chunks = kwargs.pop('chunks')
        super(DocSplitForm, self).__init__(*args, **kwargs)
        self.fields['chunk_beg'].queryset = chunks
        self.fields['chunk_end'].queryset = chunks

    class Meta:
        model = Document
        fields = ['chunk_beg', 'chunk_end']


class ChunkMoveForm(ModelForm):

    def __init__(self, *args, **kwargs):
        poss_target_docs = kwargs.pop('poss_target_docs')
        super(ChunkMoveForm, self).__init__(*args, **kwargs)
        self.fields['document'].label_from_instance = self.label_from_instance
        self.fields['document'].queryset = poss_target_docs

    @staticmethod
    def label_from_instance(obj):
        if obj.parent is not None:
            return f'{obj} (fragment nr: {obj.sequence})'
        else:
            return f'{obj} (dokument główny)'

    class Meta:
        model = Chunk
        fields = ['document']
        labels = {
            'document': 'Dokument docelowy'
        }


class ChunkMergeForm(ModelForm):
    target_chunk = ModelChoiceField(queryset=None, label="Połącz z sekcją:")

    def __init__(self, *args, **kwargs):
        poss_target_chunks = kwargs.pop('poss_target_chunks')
        super(ChunkMergeForm, self).__init__(*args, **kwargs)
        self.fields['target_chunk'].label_from_instance = self.label_from_instance
        self.fields['target_chunk'].queryset = poss_target_chunks

    @staticmethod
    def label_from_instance(obj):
        if obj:
            return obj.sequence

    class Meta:
        model = Chunk
        fields = ['target_chunk']