change_control2pred_control.py 16.5 KB
# -*- coding:utf-8 -*-

from django.contrib.auth.models import User
from django.core.management.base import BaseCommand

from dictionary.models import Change, Frame, Frame_Opinion, Lemma, Position, PositionCategory, \
    get_or_create_nkjp_arg_selection, get_or_create_nkjp_example, sortArguments, \
    sortFrameChars, sortPosCatsAsStrTab, sortPositions
from dictionary.saving import create_argument_ref, create_operation, create_phrase_type_ref, \
    reconnect_examples, update_connections
from semantics.models import LexicalUnitExamples


class Command(BaseCommand):

    def handle(self, **options):
        self.change_control2pred_control()

    def change_control2pred_control(self):
        for lemma in Lemma.objects.filter(old=False).order_by('entry_obj__name'):
            changes = {'schemata': [],
                       'schemata2change': [],
                       'schemata2check': []}
            for schema in lemma.frames.all():
                if self.schema_need_manual_check(schema):
                    changes['schemata'].append(schema)
                    changes['schemata2check'].append(schema)
                elif self.schema_need_auto_change(schema):
                    changes['schemata2change'].append(schema)
                else:
                    changes['schemata'].append(schema)

            if changes['schemata2check']:
                print u'Poprawić ręcznie:', lemma.entry_obj.name, [schema.pk for schema in changes['schemata2check']]
            if changes['schemata2change']:
                new_schemata = self.save_new_lemma_version(lemma, changes)
                print u'Zmienione:', lemma.entry_obj.name, [schema.pk for schema in new_schemata]

    def schema_need_manual_check(self, schema):
        if self.control2_type(schema):
            return True
        elif self.double_control(schema):
            return True
        return False

    def control2_type(self, schema):
        for position in schema.positions.all():
            if position.categories.filter(category__in=['controllee2', 'controller2']).exists():
                return True
        return False

    def double_control(self, schema):
        infp_control = False
        pred_control = False
        for position in schema.positions.all():
            if position.categories.filter(category__startswith='controllee').exists():
                if self.controllee_need_auto_change(position):
                    pred_control = True
                else:
                    infp_control = True
        return pred_control and infp_control

    def schema_need_auto_change(self, schema):
        for position in schema.positions.all():
            if self.controllee_need_auto_change(position):
                return True
        return False

    def controllee_need_auto_change(self, position):
        auto_change = False
        if position.categories.filter(category='controllee').exists():
            auto_change = True
            for phrase_type in position.arguments.all():
                if self.phrase_type_is_infp(phrase_type):
                    auto_change = False
        return auto_change

    def phrase_type_is_infp(self, phrase_type):
        if (phrase_type.type == 'infp'):
            return True
        if (phrase_type.type == 'lex' and
                    phrase_type.atributes.get(type='TYP FRAZY').values.all()[0].argument.type == 'infp'):
            return True
        return False

    def save_new_lemma_version(self, old_lemma, changes):
        new_schemata = []

        admin_user = User.objects.get(username='bniton')

        old_lemma.old = True
        old_lemma.save()

        # tworzenie nowej wersji hasla
        new_lemma = Lemma(entry=old_lemma.entry_obj.name,
                          entry_obj=old_lemma.entry_obj,
                          owner=old_lemma.owner,
                          phraseologist=old_lemma.phraseologist,
                          semanticist=old_lemma.semanticist,
                          vocabulary=old_lemma.vocabulary,
                          status=old_lemma.status,
                          old=False,
                          frequency_1M=old_lemma.frequency_1M,
                          frequency_300M=old_lemma.frequency_300M)
        new_lemma.save()

        # tworzenie zmiany do systemu kontroli zmian
        if old_lemma.owner:
            lemma_change = Change(user=admin_user, entry=old_lemma, act_owner=old_lemma.owner)
            lemma_change.save()
        else:
            lemma_change = Change(user=admin_user, entry=old_lemma)
            lemma_change.save()

        # przepisywanie starych wersji dla kontroli zmian i dodanie nowej
        for version in old_lemma.old_versions.all():
            new_lemma.old_versions.add(version)
        new_lemma.old_versions.add(lemma_change)

        # przepisywanie historii zmian statusow
        for status_change in old_lemma.status_history.all():
            new_lemma.status_history.add(status_change)

        # przepisywanie wiadomosci
        for message in old_lemma.messages.all():
            new_lemma.messages.add(message)

        # przepisywanie starych ramek
        for old_frame in old_lemma.old_frames.all():
            new_lemma.old_frames.add(old_frame)

        # przepisywanie ramek skladnicowych
        for skladnica_frame in old_lemma.skladnica_frames.all():
            new_lemma.skladnica_frames.add(skladnica_frame)

        # przepisywanie ramek B
        for B_frame in old_lemma.B_frames.all():
            new_lemma.B_frames.add(B_frame)

        # przepisywanie opinii o schematach
        for schema_opinion in old_lemma.frame_opinions.all():
            new_lemma.frame_opinions.add(schema_opinion)

        # przepisywanie przykladow niepasujacych do zadnego schematu
        for example in old_lemma.lemma_nkjp_examples.all():
            new_lemma.lemma_nkjp_examples.add(example)

        # dodawanie niezmienionych schematow
        for schema in changes['schemata']:
            new_lemma.frames.add(schema)

        # tworzenie nowych schematow i dolaczanie ich do czasownika
        schemata_conversions = []
        for old_schema in changes['schemata2change']:
            new_schema, positions_changes = self.get_or_create_new_schema(old_schema)
            new_schemata.append(new_schema)

            # przepinanie opinii o ramce
            try:
                old_opinion = old_lemma.frame_opinions.get(frame=old_schema)
                opinion_value = old_opinion.value
                new_lemma.frame_opinions.remove(old_opinion)
                try:
                    new_opinion = Frame_Opinion.objects.get(frame=new_schema,
                                                            value=opinion_value)
                except Frame_Opinion.DoesNotExist:
                    new_opinion = Frame_Opinion(frame=new_schema,
                                                value=opinion_value)
                    new_opinion.save()
                new_lemma.frame_opinions.add(new_opinion)
            except Frame_Opinion.DoesNotExist:
                pass

            new_lemma.frames.add(new_schema)
            schemata_conversions.append({'old_schema': old_schema,
                                         'new_schema': new_schema,
                                         'positions_changes': positions_changes})

        # przepisywanie semantyki
        sem_reconnect_operations = self.get_semantic_operations(new_lemma, schemata_conversions)
        update_connections(new_lemma.id, sem_reconnect_operations, admin_user)

        # przepisywanie/dodawanie nowych przykladow do schematow
        examples_operations = []
        for old_example in old_lemma.nkjp_examples.all():
            if new_lemma.frames.filter(pk=old_example.frame.pk).exists():
                new_lemma.nkjp_examples.add(old_example)
            else:
                conversion = (conv for conv in schemata_conversions if conv['old_schema'] == old_example.frame).next()

                argument_selections = []
                for old_arg_selection in old_example.arguments.all():

                    position_conversion = next((pos_conv for pos_conv in conversion['positions_changes']
                                                if pos_conv['from'].pk == old_arg_selection.position.pk), None)

                    if position_conversion:
                        new_arg_selection, xx = get_or_create_nkjp_arg_selection(position_conversion['to'],
                                                                                 old_arg_selection.arguments.all())
                        argument_selections.append(new_arg_selection)
                    else:
                        argument_selections.append(old_arg_selection)

                # sprawdzanie czy dany obiekt klasy NKJP_Example istnieje
                new_example, xx = get_or_create_nkjp_example(conversion['new_schema'], argument_selections,
                                                             old_example.sentence, old_example.source,
                                                             old_example.comment, old_example.opinion,
                                                             old_example.approvers.all(),
                                                             old_example.approved, old_example.semantic)
                new_lemma.nkjp_examples.add(new_example)

                # reconnect examples in semantic layer
                for frame in new_lemma.entry_obj.visible_frames(): # czy actual ??
                    for lu in frame.lexical_units.all():
                        if LexicalUnitExamples.objects.filter(lexical_unit=lu, example=old_example).exists():
                            examples_operations.append(self.disconnect_example_operation(lu, old_example))
                            examples_operations.append(self.connect_example_operation(lu, new_example))

        reconnect_examples(new_lemma, examples_operations)
        return new_schemata

    def get_or_create_new_schema(self, old_schema):
        positions = []
        positions_changes = []
        for position in old_schema.positions.all():
            if self.position_need_manual_change(position):
                new_position = self.get_or_create_new_position(position)
                positions.append(new_position)
                positions_changes.append({'from': position, 'to': new_position})
            else:
                positions.append(position)

        sorted_positions = []
        sorted_positions_dict = sortPositions(positions)
        for position_dict in sorted_positions_dict:
            sorted_positions.append(position_dict['position'])

        sorted_positions_strs = []
        for position in sorted_positions:
            sorted_positions_strs.append(position.text_rep)

        sorted_schema_chars = sortFrameChars(old_schema.characteristics.all())
        sorted_schema_chars_strs = [char.value.value for char in sorted_schema_chars]

        text_rep = u'%s:%s' % (':'.join(sorted_schema_chars_strs),
                               '+'.join(sorted_positions_strs))

        try:
            new_schema = Frame.objects.get(text_rep=text_rep)
        except Frame.DoesNotExist:
            new_schema = Frame(text_rep=text_rep)
            new_schema.save()

            last_pos_obj = None
            pos_obj_count = 0
            for pos_obj in sorted_positions:
                same_pos_db = Position.objects.filter(text_rep=pos_obj.text_rep).order_by('id')
                if not last_pos_obj or last_pos_obj.text_rep != pos_obj.text_rep:
                    pos_obj_count = 1
                    new_schema.positions.add(same_pos_db[0])
                else:
                    pos_obj_count = pos_obj_count + 1
                    if pos_obj_count <= len(same_pos_db):
                        same_pos_obj = same_pos_db[pos_obj_count - 1]
                        new_schema.positions.add(same_pos_obj)
                    else:
                        same_pos_obj = Position(text_rep=pos_obj.text_rep)
                        same_pos_obj.save()
                        for category in pos_obj.categories.all():
                            same_pos_obj.categories.add(category)
                        for arg in pos_obj.arguments.all():
                            same_pos_obj.arguments.add(arg)
                        new_schema.positions.add(same_pos_obj)
                last_pos_obj = pos_obj
            for schema_char in old_schema.characteristics.all():
                new_schema.characteristics.add(schema_char)
            if new_schema.has_phraseologic_arguments():
                new_schema.phraseologic = True
                new_schema.save()

        return new_schema, positions_changes

    def position_need_manual_change(self, position):
        if position.categories.filter(category__in=['controllee', 'controller']).exists():
            return True
        return False

    def get_or_create_new_position(self, old_position):
        categories_strs = []
        for category in old_position.categories.all():
            if category.category == 'controllee':
                categories_strs.append('pred_controllee')
            elif category.category == 'controller':
                categories_strs.append('pred_controller')
            else:
                categories_strs.append(category.category)

        sorted_categories_strs = sortPosCatsAsStrTab(categories_strs)
        sorted_arguments = sortArguments(old_position.arguments.all())

        args_strs = []
        for arg in sorted_arguments:
            args_strs.append(arg.text_rep)

        pos_text_rep = '%s{%s}' % (','.join(sorted_categories_strs), ';'.join(args_strs))

        try:
            new_position = Position.objects.get(text_rep=pos_text_rep)
        except Position.DoesNotExist:
            new_position = Position(text_rep=pos_text_rep)
            new_position.save()

            for category_name in sorted_categories_strs:
                category = PositionCategory.objects.get(category=category_name)
                new_position.categories.add(category)

            for arg in old_position.arguments.all():
                new_position.arguments.add(arg)

        return new_position

    def get_semantic_operations(self, lemma, schemata_conversions):
        operations = []

        frames = lemma.entry_obj.visible_frames() # czy actual??

        for conv in schemata_conversions:
            schema_operations = self.get_reconnect_operations(frames, conv)
            operations.extend(schema_operations)

        return operations

    def get_reconnect_operations(self, frames, conversion):
        operations = []

        for frame in frames:
            for compl in frame.complements.all():
                arg_ref = create_argument_ref(frame, compl)
                for rel in compl.realizations.all():
                    schema_change = False
                    position_change = None
                    if rel.frame.pk == conversion['old_schema'].pk:
                        schema_change = True
                        for change in conversion['positions_changes']:
                            if change['from'].pk == rel.position.pk:
                                position_change = change
                    if schema_change:
                        old_phrase_type_ref = create_phrase_type_ref(rel.frame, rel.position,
                                                                     rel.argument, rel.alternation)
                        if position_change:
                            new_phrase_type_ref = create_phrase_type_ref(conversion['new_schema'],
                                                                         position_change['to'],
                                                                         rel.argument,
                                                                         rel.alternation)
                        else:
                            new_phrase_type_ref = create_phrase_type_ref(conversion['new_schema'],
                                                                         rel.position,
                                                                         rel.argument,
                                                                         rel.alternation)
                        if new_phrase_type_ref != old_phrase_type_ref:
                            operations.append(create_operation('disconnect', arg_ref, old_phrase_type_ref))
                            operations.append(create_operation('connect', arg_ref, new_phrase_type_ref))
        return operations

    def disconnect_example_operation(self, lu, example):
        return {'operation': 'remove_example', 'unit': lu.id, 'example': example.id}

    def connect_example_operation(self, lu, example):
        return {'operation': 'add_example', 'unit': lu.id, 'example': example.id}