#-*- coding:utf-8 -*-

from django.contrib.auth.models import User
from django.db.models import * 

class Configuration(Model):
    name = CharField(max_length=16, primary_key=True, unique=True, db_column='nazwa_konfiguracji')
    selected_conf = BooleanField(db_column='wybrana_konfiguracja', default=False)
    min_1M_freq = PositiveIntegerField(db_column='minimalna_frekwencja_1M', default=3)
    min_300M_freq = PositiveIntegerField(db_column='minimalna_frekwencja_300M', default=25)
    
    def __unicode__(self):
        return '%s' % self.name             

    class Meta:
        db_table = 'konfiguracje_narzedzia'
        ordering = ['name']
        permissions = (
          ('manage_tool_configuration', u'Może zmieniać konfigurację narzędzia.'),
        )


class VocabularyFormat(Model):
    format = CharField(max_length=64, primary_key=True, unique=True, db_column='format_slownika')
    
    def __unicode__(self):
        return '%s' % self.format
  
class WalentyStat(Model):
    date = DateTimeField(auto_now_add=True, db_column='data_aktualizacji')
    label = CharField(max_length=128, db_column='etykieta')
    value = CharField(max_length=16, db_column='wartosc')
    
    def __unicode__(self):
        return '%s:\t%s' % (self.label, self.value)
   
class Vocabulary(Model):
    name = CharField(max_length=64, primary_key=True, unique=True, db_column='slownik') 
    editors = ManyToManyField(User, blank=True,
                            related_name='editable_vocabularies')
    viewers = ManyToManyField(User, blank=True,
                            related_name='visible_vocabularies')
    
    def __unicode__(self):
        return '%s' % self.name

    class Meta:
        db_table = 'slowniki'
        ordering = ['name']
        permissions = (
          ('manage_vocabulary', u'Może zarządzać słownikami'),
          ('download_vocabulary', u'Może eksportować słowniki'),
          ('view_vocab_stats', u'Może oglądać statystyki słowników'),
        )

def visible_vocabularies(user):
  if user.has_perm('dictionary.view_all_lemmas'):
    return Vocabulary.objects.all()
  else:
    return user.visible_vocabularies.all()

class Lemma_Status(Model):
    status = CharField(max_length=16, db_column='status', verbose_name=u'status')
    # okresla czy po zmianie na wskazany status trzeba walidowac schematy
    validate = BooleanField(db_column='czy_walidowac', default=False) 
    # okresla czy po zmianie na wskazany status trzeba sprawdzac czy potwierdzono przyklady wlasne
    check_examples = BooleanField(db_column='sprawdzic_przyklady', default=False)
    # okresla czy po zmianie na wskazany status trzeba sprawdzac ramki semantyczne
    check_semantics = BooleanField(db_column='sprawdzic_semantyke', default=False)
    # kolejne mozliwe statusy edycyjne
    next_statuses = ManyToManyField('Lemma_Status', related_name='prev_statuses',
                                    blank=True, null=True)
    # status edycyjny do którego przechodzi się przy cofaniu aktualnego
    abort_status = ForeignKey('Lemma_Status', related_name='nabort_statuses',
                              blank=True, null=True)
    # typ statusu
    type = ForeignKey('LemmaStatusType', related_name='lemma_statuses',
                      blank=True, null=True)
    # etap prac
    stage_of_work = ForeignKey('StageOfWork', related_name='lemma_statuses')
    # okresla kolejnosc prezentowania statusow
    priority = PositiveIntegerField(db_column='priorytet', blank=True, null=True)
    
    def __unicode__(self):
        return '%s' % (self.status) 
    
    class Meta:
      db_table = 'statusy_hasel'
      ordering = ['priority']
      permissions = (
        ('confirm_lemma', u'Może zaznaczać hasła jako sprawdzone.'),
        ('see_stats', u'Może oglądać swoje statystyki.'),
        ('see_all_stats', u'Może oglądać statystyki wszystkich.'),
      )
      
def get_checked_statuses():
    checked_type = LemmaStatusType.objects.get(sym_name='checked')
    return Lemma_Status.objects.filter(type__priority__gte=checked_type.priority).distinct()
      
def get_ready_statuses():
    ready_type = LemmaStatusType.objects.get(sym_name='ready')
    return Lemma_Status.objects.filter(type__priority__gte=ready_type.priority).distinct()
 
      
class LemmaStatusType(Model):
    # nazwa statusu
    name = CharField(max_length=16, db_column='nazwa', verbose_name=u'nazwa')
    # symboliczna nazwa statusu
    sym_name = CharField(max_length=16, db_column='symboliczna_nazwa', verbose_name=u'symboliczna_nazwa')
    # okresla kolejnosc prezentowania typow statusow
    priority = PositiveIntegerField(db_column='priorytet', blank=True, null=True)
    
    def __unicode__(self):
        return '%s' % (self.name)
    
    class Meta:
        ordering = ['priority']
        
# zakres pracy dla ktorego obowiazuje status
class StageOfWork(Model):
    name = CharField(max_length=16, db_column='nazwa', verbose_name=u'nazwa')
    sym_name = CharField(max_length=16, db_column='symboliczna_nazwa', verbose_name=u'symboliczna_nazwa')
    priority = PositiveIntegerField(db_column='priorytet', blank=True, null=True)
    
    def __unicode__(self):
        return '%s' % (self.name)
    
    class Meta:
        ordering = ['priority']

class Lemma(Model):
    id = AutoField(primary_key=True, db_column='l_id')
    # czasownik w bezokoliczniku --> musi zostac wywalone, entry bedzie pochodzilo z obiektow modelu Entry
    entry = CharField(max_length=64, db_column='haslo', db_index=True,
                      verbose_name=u'hasło')
    # wpis w slowniku --> koniecznie to musi zostac dodane jako glowne powiazanie z entry
    entry_obj = ForeignKey('Entry', db_column='wpis', 
                            related_name='lemmas')
    # wlasciciel hasla
    owner = ForeignKey(User, blank=True, null=True, db_column='wlasciciel',
                       related_name='lemmas')
    # przypisany frazeolog
    phraseologist = ForeignKey(User, blank=True, null=True, db_column='frazeolog',
                               related_name='phraseologist_lemmas')
    # przypisany semantyk
    semanticist = ForeignKey(User, blank=True, null=True, db_column='semantyk',
                               related_name='semanticist_lemmas')
    # slownik do ktorego nalezy
    vocabulary = ForeignKey('Vocabulary', db_column='slownik', 
                            related_name='lemmas')
    # status obrobki hasla
    status = ForeignKey('Lemma_Status', db_column='status', 
                       related_name='lemmas')
    # osoba blokujaca haslo na czas zapisu
    locker = ForeignKey(User, blank=True, null=True, db_column='osoba_zapisujaca',
                       related_name='locked_lemmas')
    # liczba wystapien lematu w 300 milionowym podkorpusie NKJP
    frequency_300M = PositiveIntegerField(db_column='frekwencja_300M', blank=True, 
                                          null=True, default=0)
    # liczba wystapien lematu w 1 milionowym podkorpusie NKJP
    frequency_1M = PositiveIntegerField(db_column='frekwencja_1M', blank=True, 
                                        null=True, default=0)
    # dawne wersje hasla
    old_versions = ManyToManyField('Change', db_table='haslo_stareWersje',
                                   blank=True, null=True, related_name='lemmas')
    # czy haslo jest aktualne czy nie (pomocne w systemie kontroli zmian)
    old = BooleanField(db_column='nieaktualny') 
    #schematy
    frames = ManyToManyField('Frame', db_table='haslo_ramki', blank=True, null=True,
                             related_name='lemmas')
    # stare schematy
    old_frames = ManyToManyField('Old_Frame', db_table='haslo_stareRamki',
                                 blank=True, null=True, related_name='lemmas')
    #schematy ze składnicy
    skladnica_frames = ManyToManyField('Old_Frame', db_table='haslo_ramkiSkladnicowe',
                                       blank=True, null=True, related_name='lemmas_skladnica')
    #schematy B
    B_frames = ManyToManyField('B_Frame', db_table='haslo_ramkiB',
                               blank=True, null=True, related_name='lemmas_B')
    # przyklady z NKJP dla ramek hasla
    nkjp_examples = ManyToManyField('NKJP_Example', db_table='haslo_ramkiNkjp',
                                    blank=True, null=True, related_name='lemmas')
    # oceny ramek dla ramek hasla
    frame_opinions = ManyToManyField('Frame_Opinion', db_table='lemma_ramkiOpinie',
                                     blank=True, null=True, related_name='lemmas')
    # przyklady przyporzadkowane do hasla jako calosci (pola frame i atributes beda niewypelniane)
    lemma_nkjp_examples = ManyToManyField('NKJP_Example', db_table='haslo_Nkjp',
                                    blank=True, null=True, related_name='lemmasSelf')
    # historia zmiany statusów hasła
    status_history = ManyToManyField('StatusChange', db_table='historia_statusow',
                                    blank=True, null=True, related_name='lemmas')
    
    def __unicode__(self):
        return '%s (%s)' % (self.entry, self.vocabulary)
  
    def get_frames_by_char_values(self, reflex_val, neg_val, pred_val, aspect_val):
        matching_frames = self.frames.filter(characteristics__value=reflex_val
                                             ).filter(characteristics__value=neg_val
                                                      ).filter(characteristics__value=pred_val
                                                               ).filter(characteristics__value=aspect_val)
        return matching_frames
    
    def get_existing_frame_char_values(self, char_type):
        char_vals = []
        for frame in self.frames.all():
            char_value = frame.get_char_value(char_type)
            if not char_value in char_vals:
                char_vals.append(char_value.pk)
        char_vals_query = Frame_Char_Value.objects.filter(pk__in=char_vals)
        return char_vals_query.order_by('-priority')
    
    def contains_frame_with_exact_positions(self, positions):
        contains = False
        frames = self.frames
        for position in positions:
            frames = frames.filter(positions__text_rep=position.text_rep)
        for frame in frames.all():
            if frame.positions.count() == len(positions):
                contains = True
                break   
        return contains
    
    def get_schema_opinion(self, frame):
        frame_opinion_name = ''
        try:
            frame_opinion = self.frame_opinions.get(frame=frame)
            frame_opinion_name = unicode(frame_opinion)
        except Frame_Opinion.DoesNotExist:
            pass
        return frame_opinion_name
    
    def phraseology_ready(self):
        actual_status = self.status
        ready_f_status = Lemma_Status.objects.get(type__sym_name='ready_f')
        if actual_status.priority >= ready_f_status.priority:
            return True
        return False
        
    def semantics_ready(self):
        actual_status = self.status
        ready_s_status = Lemma_Status.objects.get(type__sym_name='ready_s')
        if actual_status.priority >= ready_s_status.priority:
            return True
        return False
        
    class Meta:
      db_table = 'hasla'
      permissions = (
        ('view_lemma', u'Może oglądać leksemy'), 
        ('change_all_lemmas', u'Może zmieniać wszystkie hasła'),
        ('change_lemmas', u'Może zmieniać hasła, których jest właścicielem'),
        ('add_phraseologic_frames', u'Może dodawać ramki frazeologiczne'),
        ('add_syntactic_frames', u'Może dodawać ramki syntaktyczne'),
        ('add_semantic_frames', u'Może dodawać ramki semantyczne'),
        ('own_lemmas', u'Może posiadać hasła'),
      )
      
class B_Frame(Model):
    # tekst ramki zgodnie ze skladnica
    text_rep = TextField(db_column='tekst_rep', verbose_name=u'reprezentacja tekstowa')
    # identyfikator tekstowy ramki
    text_id = TextField(db_column='tekst_id', verbose_name=u'identyfikator tekstowy', unique=True)
    # zwrotnosc
    reflex = BooleanField(db_column='zwrotnosc')
    # aspect
    aspect = ForeignKey('Frame_Characteristic', db_column='aspekt', related_name='B_frames_aspect')
    # negatywnosc
    negativity = ForeignKey('Frame_Characteristic', db_column='negatywnosc', related_name='B_frames_negativity')
    # przechodniość
    trans = BooleanField(db_column='przechodniosc')
    # niewlasciwa
    impr = BooleanField(db_column='niewlasciwosc')
    # argumenty starej ramki
    arguments = ManyToManyField('B_Argument', related_name='B_frames', null=True, blank=True)
    
    def __unicode__(self):
        return self.text_rep
 
    
class B_Argument(Model):
    # reprezentacja tekstowa argumentu, zgodnie z notacja skladnicowa
    text_rep = CharField(max_length=64, db_column='reprezentacja_tekstowa', 
                         verbose_name=u'reprezentacja tekstowa')
    # identyfikator tekstowy argumentu zgodnie z notacja B
    text_id = CharField(max_length=64, db_column='identyfikator_tekstowy', 
                        verbose_name=u'identyfikator_tekstowy', unique=True)
#    # wyrazenie regularne reprezentujace argument
#    regex = CharField(max_length=64, db_column='regex', 
#                      verbose_name=u'wyrazenie_regularne')
    # możliwe argumenty
    possible_args = ManyToManyField('Arg_Possibility',
                                    related_name='B_args',
                                    null=True, blank=True)
    
    def __unicode__(self):
        return u'%s' % (self.text_rep)


class StatusChange(Model):
    """Model representing lemma status changes."""
    # data dokonania zmiany statusu
    date = DateTimeField(auto_now_add=True, db_column='data_zmiany')
    # aktualny wlasciciel hasla
    act_owner = ForeignKey(User, db_column='aktualny_wlasciciel', 
                           related_name='status_changes',
                           blank=True, null=True)
    # osoba ktora zmienila status hasla
    changer = ForeignKey(User, db_column='zmieniajacy', 
                           related_name='chn_status_changes',
                           blank=True, null=True)
    # aktualna postac hasla, po zmianie statusu
    lemma = ForeignKey(Lemma, db_column='postac_hasla', related_name='status_changes',
                       blank=True, null=True)
    # ramy semantyczne w momencie zmiany statusu
    semantic_frames = ManyToManyField('semantics.SemanticFrame',
                                      blank=True, null=True, related_name='status_changes')
    # osiagniety status
    status = ForeignKey(Lemma_Status, db_column='status', 
                        related_name='status_changes',
                        blank=True, null=True)
    
    def __unicode__(self):
        return self.lemma.entry + ':' + str(self.date)
  
    class Meta:
        permissions = (
            ('view_status_changes', u'Może oglądać historię zmian statusu'),
        )

class Frame_Opinion(Model):
    frame = ForeignKey('Frame', db_column='ramka', related_name='opinions',
                       blank=True, null=True)
    value = ForeignKey('Frame_Opinion_Value', db_column='opinia', related_name='opinions')
    
    def __unicode__(self):
        return self.value.value


class Frame_Opinion_Value(Model):
    value = CharField(max_length=16, db_column='wartosc', 
                         verbose_name=u'wartosc', unique=True)
    short = CharField(max_length=16, db_column='nazwa_skrocona', 
                         verbose_name=u'nazwa_skrocona', blank=True, 
                         null=True)
    # okresla kolejnosc prezentowania opinii
    priority = PositiveIntegerField(db_column='priorytet', blank=True, null=True)
    
    def __unicode__(self):
      return '%s' % (self.value)


class Frame(Model):
    id = AutoField(primary_key=True, db_column='f_id')
    # pozycje skladniowe
    positions = ManyToManyField('Position', db_table='ramka_pozycje', 
                                blank=True, null=True, related_name='frames')
    # charakterystyki ramy
    characteristics = ManyToManyField('Frame_Characteristic', db_table='cechy_ramy',
                                      blank=True, null=True, related_name='frames')  
    # reprezentacja tekstowa
    text_rep = TextField(db_column='tekst_rep', verbose_name=u'reprezentacja tekstowa', unique=True)
    # czy ramka bazowa (widoczna w interfejsie dodawania nowych ramek jako bazowa)
    base = BooleanField(db_column='ramka_bazowa', default=False)
    # czy ramka jest frazeologiczna
    phraseologic = BooleanField(db_column='frazeologiczna', default=False)
    
    def char_exists(self, char_type, value):
        return self.characteristics.filter(type=char_type, 
                                           value__value=value).exists()
                                           
    def has_phraseologic_arguments(self):
        has_phraseologic_arguments = False
        phraseologic_arg_models = Argument_Model.objects.filter(phraseologic=True)
        phraseologic_arg_models_names = [arg_model.arg_model_name for arg_model in phraseologic_arg_models.all()]
        if (self.positions.filter(arguments__type__in=phraseologic_arg_models_names).exists() or
            self.positions.filter(arguments__atributes__values__argument__type__in=phraseologic_arg_models_names).exists()):
            has_phraseologic_arguments = True
        return has_phraseologic_arguments
    
    def has_inactive_arguments(self):
        has_inactive_arguments = False
        inactive_arg_models = Argument_Model.objects.filter(active=False)
        inactive_arg_models_names = [arg_model.arg_model_name for arg_model in inactive_arg_models.all()]
        if self.positions.filter(arguments__type__in=inactive_arg_models_names).exists():
            has_inactive_arguments = True
        return has_inactive_arguments
    
    def is_fully_lexicalized(self):
        is_fully_lexicalized = True
        for position in self.positions.all():
            if not position.is_fully_lexicalized():
                is_fully_lexicalized = False
                break
        return is_fully_lexicalized
    
    def get_char_value(self, char_type):
        return self.characteristics.get(type=char_type).value
    
    def get_position_spaced_text_rep(self):
        sorted_frame_chars = sortFrameChars(self.characteristics.all())
        sorted_positions = sortPositions(self.positions.all())
        frame_chars_str_tab = [char.value.value for char in sorted_frame_chars]
        positions_str_tab = [position['position'].text_rep for position in sorted_positions]
        return u'%s:%s' % (':'.join(frame_chars_str_tab), ' + '.join(positions_str_tab))
    
    def __unicode__(self):
        return '%s' % (self.text_rep)
    
def positions_to_frame(positions, reflex, negativity, predicativity, aspect):
    frame_chars = []
    frame_chars.append(Frame_Characteristic.objects.get(type=u'ZWROTNOŚĆ', value__value=reflex))
    frame_chars.append(Frame_Characteristic.objects.get(type=u'NEGATYWNOŚĆ', value__value=negativity))
    frame_chars.append(Frame_Characteristic.objects.get(type=u'PREDYKATYWNOŚĆ', value__value=predicativity))
    frame_chars.append(Frame_Characteristic.objects.get(type=u'ASPEKT', value__value=aspect))

    frame_chars = sortFrameChars(frame_chars)
        
    frame_char_vals_str_ls = list([frame_char.value.value for frame_char in frame_chars])
    sorted_positions = sortPositions(positions)
    sort_pos_str_tab = [position['position'].text_rep for position in sorted_positions]
    
    frame_text_rep = u'%s:%s' % (':'.join(frame_char_vals_str_ls), 
                                 '+'.join(sort_pos_str_tab))
    try: 
        frame_obj = Frame.objects.get(text_rep=frame_text_rep)
    except Frame.DoesNotExist:
        frame_obj = Frame(text_rep=frame_text_rep)
        frame_obj.save()
        frame_obj.positions.add(*positions)
        frame_obj.characteristics.add(*frame_chars)
        if frame_obj.has_phraseologic_arguments():
            frame_obj.phraseologic = True
            frame_obj.save()
    return frame_obj

def get_schemata_by_type(sch_type, schemata_query):
    if sch_type == 'normal':
        schemata_query = get_normal_schemata_only(schemata_query)
    elif sch_type == 'phraseologic':
        schemata_query = get_phraseologic_schemata_only(schemata_query)
    return schemata_query

def get_normal_schemata_only(schemata_query):
    schemata_query = schemata_query.filter(phraseologic=False)
    return schemata_query

def get_phraseologic_schemata_only(schemata_query):
    schemata_query = schemata_query.filter(phraseologic=True)
    return schemata_query


class NKJP_Example(Model):
    # ramka ktorej dotyczy przyklad
    frame = ForeignKey(Frame, db_column='ramka', related_name='nkjp_examples',
                       blank=True, null=True)
    # argumenty ramki, ktorych dotyczy przyklad
    arguments = ManyToManyField('NKJP_ArgSelection', db_table='przykladNKJP_argumenty',
                                blank=True, null=True, related_name='nkjp_examples')
    # przykladowe zdanie
    sentence = TextField(db_column='zdanie', verbose_name=u'zdanie')
    # zrodlo
    source = ForeignKey('NKJP_Source', db_column='zrodlo', related_name='nkjp_examples')
    # komentarz do przykladu
    comment = TextField(db_column='komentarz', verbose_name=u'komentarz')#,
                        #blank=True, null=True)
    # ocena przykladu
    opinion = ForeignKey('NKJP_Opinion', db_column='opinia', related_name='nkjp_examples')
    # osoby ktore zatwierdzily haslo
    approvers = ManyToManyField(User, db_table='osoby_zatwierdzajace',
                                blank=True, null=True, related_name='nkjp_examples')
    # jesli przyklad wlasny, to czy zostal w pelni zatwierdzony
    approved = BooleanField(db_column='zatwierdzony', default=True)
    
    # czy przyklad zostal dodany w trakcie prac semantycznych
    semantic = BooleanField(db_column='semantyczny', default=False)
    
    
    def __unicode__(self):
        return '%s' % (self.sentence)
  
    def pinned_to(self, arg_text_rep):
        if self.arguments.filter(arguments__text_rep=arg_text_rep):
            return True
        return False
  
    class Meta:
        db_table = 'nkjp_przyklad'
        permissions = (
          ('confirm_example', u'Może potwierdzać przykłady.'),
          ('modify_lemma_examples', u'Może modyfikować przykłady niedowiązane'),
          ('modify_all_examples', u'Może dowolnie modyfikować przykłady'),
          
          ('add_semantic_examples', u'Może dodawać przykłady semantyczne'), 
          ('delete_semantic_examples', u'Może usuwać przykłady semantyczne'),
        )
      
def get_or_create_nkjp_example(frame, arguments, sentence, source,
                                   comment, opinion, approvers, approved):
    created = False
    example = None
    possible_examples = NKJP_Example.objects.filter(frame=frame, sentence=sentence,
                                                    source=source, comment=comment,
                                                    opinion=opinion, approved=approved)
    if possible_examples.exists():
        for arg_sel in arguments:
            possible_examples = possible_examples.filter(arguments=arg_sel)
        for approver in approvers:
            possible_examples = possible_examples.filter(approvers=approver)
        if possible_examples.exists():
            for poss_example in possible_examples.all():
                if (poss_example.approvers.count() == len(approvers) and
                    poss_example.arguments.count() == len(arguments)):
                    example = poss_example
                    break
            if not example:
                example = create_nkjp_example(frame=frame, arguments=arguments, 
                                              sentence=sentence, source=source,
                                              comment=comment, opinion=opinion, 
                                              approvers=approvers, approved=approved)
                created = True
        else:
            example = create_nkjp_example(frame=frame, arguments=arguments, 
                                          sentence=sentence, source=source,
                                          comment=comment, opinion=opinion, 
                                          approvers=approvers, approved=approved)
            created = True
    else:
        example = create_nkjp_example(frame=frame, arguments=arguments, 
                                      sentence=sentence, source=source,
                                      comment=comment, opinion=opinion, 
                                      approvers=approvers, approved=approved)
        created = True
    return example, created
        
def create_nkjp_example(frame, arguments, sentence, source,
                           comment, opinion, approvers, approved):
    example = NKJP_Example(frame=frame, sentence=sentence,
                           source=source, comment=comment,
                           opinion=opinion, approved=approved)
    example.save()
    example.arguments.add(*arguments)
    example.approvers.add(*approvers)
    return example
      
class NKJP_ArgSelection(Model):
    # pozycja na ktorej wystepuja zaznaczone argumenty
    position = ForeignKey('Position', db_column='pozycja', related_name='nkjp_argSelections',
                          blank=True, null=True)
    # zaznaczone argumenty
    arguments = ManyToManyField('Argument', db_table='wyborArgNKJP_argumenty',
                                blank=True, null=True, related_name='nkjp_argSelections')
    
    def __unicode__(self):
      arguments = [arg.text_rep for arg in self.arguments.all()] 
      return '[%s <%s>]' % (self.position.text_rep, u';'.join(arguments))
  
def get_or_create_nkjp_arg_selection(position, arguments):
    created = False
    nkjp_arg_selection = None
    possible_selections = NKJP_ArgSelection.objects.filter(position=position)
    if possible_selections.exists():
        for arg in arguments:
            possible_selections = possible_selections.filter(arguments=arg)
        if possible_selections.exists():
            for poss_sel in possible_selections:
                if poss_sel.arguments.count() == len(arguments):
                    nkjp_arg_selection = poss_sel
                    break
            if not nkjp_arg_selection:
                nkjp_arg_selection = create_nkjp_arg_selection(position, arguments)
                created = True
        else:
            nkjp_arg_selection = create_nkjp_arg_selection(position, arguments)
            created = True
    else:
        created = True
        nkjp_arg_selection = create_nkjp_arg_selection(position, arguments)
    return nkjp_arg_selection, created

def create_nkjp_arg_selection(position, arguments):
    nkjp_arg_selection = NKJP_ArgSelection(position=position)
    nkjp_arg_selection.save()
    nkjp_arg_selection.arguments.add(*arguments)
    return nkjp_arg_selection
    
      
class NKJP_Source(Model):
    source = CharField(max_length=64, db_column='zrodlo', 
                         verbose_name=u'zrodlo', unique=True)
    sym_name = CharField(max_length=32, db_column='nazwa_symboliczna', default='NKJP')
    # flaga oznaczajaca czy dane zrodlo wymaga dodatkowego komentarza
    comment_required = BooleanField(db_column='wymagany_komentarz', default=False)
    # flaga oznaczajaca czy przyklad pochodzacy z dango zrodla powinien zostac potwierdzony
    confirmation_required = BooleanField(db_column='wymagane_potwierdzenie', default=False)
    # okresla kolejnosc prezentowania zrodel przykladu
    priority = PositiveIntegerField(db_column='priorytet', blank=True, null=True)
    
    class Meta:
        ordering = ['priority']
    
    def __unicode__(self):
        return '%s' % (self.source)
 
  
class NKJP_Opinion(Model):
    opinion = CharField(max_length=16, db_column='opinia', 
                         verbose_name=u'opinia', unique=True)
    # okresla kolejnosc prezentowania opinii o przykladzie na liscie rozwijanej
    priority = PositiveIntegerField(db_column='priorytet', blank=True, null=True)
    
    class Meta:
      ordering = ['priority']
    
    def __unicode__(self):
      return '%s' % (self.opinion)
 
      
class Position(Model):
    # kategoria pozycji
    categories = ManyToManyField('PositionCategory', db_table='pozycja_kategorie', null=True, blank=True,
                             related_name='positions')
    # argumenty pozycji
    arguments = ManyToManyField('Argument', db_table='pozycja_argumenty',
                                blank=True, null=True, related_name='positions')
    # reprezentacja tekstowa                                                 # UWAGA, TUTAJ swieze, wywalono unique
    text_rep = TextField(db_column='tekst_rep', verbose_name=u'reprezentacja tekstowa')#, unique=True)
    # liczba wystapien
    occurrences = PositiveIntegerField(db_column='wystapienia', default=0)
    
    def is_fully_lexicalized(self):
        is_fully_lexicalized = True        
        for argument in self.arguments.all():
            if not argument.is_fully_lexicalized():
                is_fully_lexicalized = False
                break
        return is_fully_lexicalized
    
    def ordered_categories(self):
        return self.categories.order_by('priority')
    
    def __unicode__(self):
      return '%s' % (self.text_rep)
    
    class Meta:
      db_table = 'pozycje_skladniowe'
      
def get_or_create_position(categories=[], arguments=[]): # nie uwzglednia kontroli, dodac w razie czego
    arguments = list(set(arguments))
    arguments = sortArguments(arguments)
    categories_str_tab = [category.category for category in categories]
    categories_str_tab = sortPosCatsAsStrTab(categories_str_tab)
    args_str_tab = [arg.text_rep for arg in arguments]   
    pos_text_rep = '%s{%s}' % (','.join(categories_str_tab),';'.join(args_str_tab))
    pos_objs = Position.objects.filter(text_rep=pos_text_rep) 
    if pos_objs.count() > 0:
        pos_obj = pos_objs.all()[0]
    else:
        pos_obj = Position(text_rep=pos_text_rep)
        pos_obj.save()
        pos_obj.arguments.add(*arguments)
        pos_obj.categories.add(*categories)
    return pos_obj

def sort_positions(positions):
    sorted_positions_dicts = sortPositions(positions)
    return [position['position'] for position in sorted_positions_dicts]
      
class PositionCategory(Model):
    category = CharField(max_length=16, db_column='kategoria', 
                         verbose_name=u'typ', unique=True)
    # czy kategoria jest zwiazana z kontrola
    control = BooleanField(db_column='kontrola')
    #priorytet prezentowania danych
    priority = PositiveIntegerField(db_column='priorytet', blank=True, null=True)
    
    def __unicode__(self):
        return '%s' % (self.category)
  
    
class Argument(Model): 
    type = CharField(max_length=16, db_column='typ_argumentu', 
                     verbose_name=u'typ argumentu')
    atributes = ManyToManyField('Atribute', db_table='argument_atrybuty', 
                                null=True, blank=True, related_name='arguments')
    # reprezentacja tekstowa
    text_rep = TextField(db_column='tekst_rep', verbose_name=u'reprezentacja tekstowa', unique=True)
    realizations = ManyToManyField('ArgRealization', db_table='realizacje', 
                                   related_name='arguments', null=True, blank=True)
    
    def model_used_for_limitations(self):
        model_for_limitations = Argument_Model.objects.get(arg_model_name=self.type)
        if model_for_limitations.use_subarg_for_limitations:
            sub_arg_model_name = self.atributes.all()[0].values.all()[0].argument.type
            model_for_limitations = Argument_Model.objects.get(arg_model_name=sub_arg_model_name)
        return model_for_limitations
    
    def is_phraseologic(self):
        arg_model = Argument_Model.objects.get(arg_model_name=self.type)
        return arg_model.phraseologic
        
    def is_fully_lexicalized(self):
        arg_model = Argument_Model.objects.get(arg_model_name=self.type)
        if arg_model.phraseologic:
            is_fully_lexicalized = self.__is_phraseologic_and_fully_lexicalized()
        elif self.atributes.filter(type=u'KATEGORIA').exists(): #xp
            is_fully_lexicalized = self.__have_subtypes_and_is_fully_lexicalized()
        else:
            is_fully_lexicalized = False
        return is_fully_lexicalized
    
    def __is_phraseologic_and_fully_lexicalized(self):
        if self.atributes.filter(type=u'MODYFIKACJA').exists():
            modification_attr = self.atributes.get(type=u'MODYFIKACJA')
            if modification_attr.selection_mode.sym_name == 'natr':
                is_fully_lexicalized = True
            elif modification_attr.values.exists():
                is_fully_lexicalized = True
                for pos_val in modification_attr.values.all():
                    if not pos_val.position.is_fully_lexicalized():
                        is_fully_lexicalized = False
                        break
            else:
                is_fully_lexicalized = False
        elif self.atributes.filter(type=u'TYPY FRAZ').exists(): # compar
            is_fully_lexicalized = True
            arguments_attr = self.atributes.get(type=u'TYPY FRAZ')
            for arg_val in arguments_attr.values.all():
                if not arg_val.argument.is_fully_lexicalized():
                    is_fully_lexicalized = False
                    break
        else:
            is_fully_lexicalized = True
        return is_fully_lexicalized  
    
    def __have_subtypes_and_is_fully_lexicalized(self):
        is_fully_lexicalized = True
        category_attr = self.atributes.get(type=u'KATEGORIA')
        if category_attr.values.count() == 0: # xp bez podtypow
            is_fully_lexicalized = False
        else:
            for arg_val in category_attr.values.all():
                if not arg_val.argument.is_fully_lexicalized():
                    is_fully_lexicalized = False
                    break
        return is_fully_lexicalized
    
    def __unicode__(self):
        return '%s' % (self.text_rep)
   
    def contains_parameter_attribute(self, attr_type_name, param_model_name):
        return self.atributes.filter(type=attr_type_name,
                                     values__parameter__type__name=param_model_name).exists()
                                     
    def has_real_only_parameter(self):
        for attr in self.atributes.all():
            for value in attr.values.filter(type__sym_name=u'parameter'):
                if value.parameter.type.realization_only:
                    return True
        return False
    
    def main_phrase_type(self):
        category_attrs = self.atributes.filter(type=u'KATEGORIA')
        if not category_attrs.exists() or category_attrs.all()[0].values.count() == 0: # xp bez podtypow
            return self
        else:
            return Argument.objects.get(text_rep=u'%s(%s)' % (self.type, category_attrs.all()[0].selection_mode.name))
    
    class Meta:
      permissions = (
        ('view_realization', u'Może oglądać realizacje argumentów.'),
        ('create_realization', u'Może kreować realizacje argumentów.'),
        ('view_arg_stats', u'Może oglądać statystyki argumentów.'),
      ) 

def sort_arguments(arguments):
    return sortArguments(arguments)

def reflex_phrase_types():
    return ['refl', 'recip']               
    
class ArgRealization(Model):
    type = ForeignKey('RealizationType', related_name='realizations')
    argument = ForeignKey('Argument', blank=True, null=True)
    positions = ManyToManyField('Position', blank=True, null=True, 
                                related_name='realizations')
    opinion = ForeignKey('ArgRealOpinion', related_name='realizations')
    
    def is_used(self):
        if self.arguments.exists():
            return True
        return False
    
    def __unicode__(self):
        text_rep = ''
        if self.type.sym_name == 'phrase_type':
            text_rep = self.argument.text_rep
        elif self.type.sym_name == 'positions':
            sorted_positions = sort_positions(self.positions.all())
            positions_str_tab = [unicode(position) for position in sorted_positions]
            text_rep = ' + '.join(positions_str_tab)
        return text_rep
    
def get_or_create_extension(extension_type, phrase_type, positions, opinion):
    created = False
    if extension_type.sym_name == 'phrase_type':
        extension, created = get_or_create_phrase_type_extension(phrase_type, opinion)
    elif extension_type.sym_name == 'positions':
        extension, created = get_or_create_positions_extension(positions, opinion)
    return extension, created

def get_or_create_phrase_type_extension(phrase_type, opinion):
    extension_type = RealizationType.objects.get(sym_name='phrase_type')
    extension, created = ArgRealization.objects.get_or_create(opinion=opinion,
                                                         argument=phrase_type,
                                                         type=extension_type)
    return extension, created
    
def get_or_create_positions_extension(positions, opinion):
    created = False
    extension = None
    extension_type = RealizationType.objects.get(sym_name='positions')
    extensions = ArgRealization.objects.annotate(positions_count=Count('positions'))
    extensions = extensions.filter(positions_count=len(positions),
                                   opinion=opinion,
                                   type=extension_type)
    for position in positions:
        extensions = extensions.filter(positions=position)
    if extensions.exists():
        extension = extensions.all()[0]
    else:
        extension = ArgRealization(opinion=opinion, type=extension_type)
        extension.save()
        extension.positions.add(*positions)
        created = True
    return extension, created
  
class RealizationType(Model):
    name = CharField(max_length=24)
    sym_name = CharField(max_length=24)
    priority = PositiveIntegerField(db_column='priorytet')
    
    def __unicode__(self):
        return self.name
    
class ArgRealOpinion(Model):
    value = CharField(max_length=16, db_column='wartosc', unique=True)
    priority = PositiveIntegerField(db_column='priorytet')
    
    def __unicode__(self):
        return self.value
    
class Argument_Model(Model):
    # nazwa typu argumentu
    arg_model_name = CharField(max_length=16, db_column='nazwa_modelu', 
                               verbose_name=u'nazwa modelu', primary_key=True, unique=True)
    # mozliwe wartosci przyjmowane przez atrybut o nazwie model_name
    atribute_models = ManyToManyField('Atribute_Model', db_table='wzory_atrybutow',
                                      related_name='argument_models', 
                                      blank=True, null=True) # to pole jednak nie jest wymagane, patrz refl, epsilon
    # czy wystepuje tylko jako realizacja xp, lp, comprepnp 
    # nie ma juz typow argumentow wystepujacych tylko jako realizacje
    realization_only = BooleanField(db_column='tylko_realizacja', default=False)
    has_realizations = BooleanField(db_column='posiada_realizacje', default=False)
    # czy wystepuje tylko wewnatrz argumentow zleksykalizowanych
    lex_only = BooleanField(db_column='tylko_w_leksykalizacjach', default=False)
    # wystepuje wewnatrz argumentow zleksykalizowanych (lex, fixed)
    used_in_lex = BooleanField(db_column='wykorzystywany_w_leksykalizacjach', default=False)
    # czy typ argumentu ma byc widoczny w reprezentacji tekstowej
    hide_type = BooleanField(db_column='ukryty_typ', default=False)
    # modele parametrow nie przyjmowane przez model argumentu
    exclude_param_models = ManyToManyField('AttributeParameterModel', db_table='wylaczone_parametry',
                                           related_name='argument_models', 
                                           blank=True, null=True)
    # czy argument jest frazeologiczny
    phraseologic = BooleanField(db_column='argument_frazeologiczny', default=False)
    # leksykalizacja (frazeologia) wykluczone modele atrybutow dla wybranego parametru (TYP FRAZY)
    sec_attr_limitations = ManyToManyField('Atribute_Model', db_table='wtornie_wykluczone_atrybuty',
                                           blank=True, null=True)
    # wykorzystaj podtyp (np. dla xp) by ograniczyc atrybuty we frazeologii
    use_subarg_for_limitations = BooleanField(db_column='wyklucz_przez_podtyp', default=False)
    # czy model jest aktywny
    active = BooleanField(db_column='aktywny', default=True)
    # mozna wybierac posrod wszystkich wartosci atrybutow przy tworzeniu argumentow zleksykalizowanych (tez fixed)
    all_attr_params_used_in_lex = BooleanField(default=False)
    # mozliwe czesci mowy
    poss = ManyToManyField('POS', db_table='czesci_mowy_modele_argumentow',
                           related_name='argument_models')
    # mozliwe tagi dla atrybutu LEMAT LICZEBNIKA, jeśli dotyczy (walidacja morfeuszowa)
    possible_num_lemma_tags = ManyToManyField('NkjpPosTag', related_name='argument_models_num',
                                              db_table='tagi_liczebnikow_modele_argumentow', 
                                              blank=True, null=True)
    # mozliwe tagi dla atrybutu LEMAT, jeśli dotyczy (walidacja morfeuszowa)
    possible_lemma_tags = ManyToManyField('NkjpPosTag', related_name='argument_models',
                                          db_table='tagi_lematow_modele_argumentow', 
                                          blank=True, null=True)
    # priorytet prezentowania argumentow w pozycji
    priority = PositiveIntegerField(db_column='priorytet', blank=True, null=True)
    
    def get_possible_lemma_tags(self, attr_model):
        lemma_tags = NkjpPosTag.objects.none()
        if attr_model.sym_name == 'lemma':
            lemma_tags = self.possible_lemma_tags.all()
        elif attr_model.sym_name == 'numeral_lemma':
            lemma_tags = self.possible_num_lemma_tags.all()
        return lemma_tags
    
    def __unicode__(self):
        return '%s' % (self.arg_model_name)
  
class Atribute_Model(Model):
    # typ atrybutu
    type = ForeignKey('AttributeType', db_column='typ', related_name='attribute_models',
                      default=1)
    # nazwa typu atrybutu
    atr_model_name = CharField(max_length=32, db_column='nazwa_atrybutu', 
                               verbose_name=u'nazwa modelu atrybutu')
    # nazwa modelu wykorzytywana przy generacji xmla
    sym_name = CharField(max_length=32, db_column='nazwa_skrocona', 
                         verbose_name=u'nazwa_skrocona', blank=True, 
                         null=True)
    # mozliwe modele parametrow
    possible_parameters = ManyToManyField('AttributeParameterModel', db_table='przyjmowane_parametry', 
                                          null=True, blank=True, related_name='attribute_models')
    # sposob sortowania wartosci w interfejsie realizacji
    realizations_values_order = CharField(max_length=32, db_column='sortowanie_wartosci_realizacje', 
                                          default='priority')
    # sposob sortowania wartosci w interfejsie edycji hasla
    entry_edit_values_order = CharField(max_length=32, db_column='sortowanie_wartosci_edycja',
                                        default='priority')
    # tryb wybierania argumentów z listy
    values_selection_modes = ManyToManyField('AttrValueSelectionMode', db_table='tryb_wybierania_wartosci', 
                                             null=True, blank=True, related_name='attribute_models')
    # mozliwe znaki oddzielające kolejne wartosci
    value_separators = ManyToManyField('AttrValuesSeparator', db_table='modele_atrybutow_do_separatorow_wartosci', 
                                       null=True, blank=True, related_name='atribute_models')
    # znak konczacy liste wartosci atrybutu
    val_list_start = CharField(max_length=1, db_column='poczatek_listy_wartosci', 
                               default='(', null=True, blank=True)
    # znak zamykajacy liste wartosci atrybutu
    val_list_end = CharField(max_length=1, db_column='koniec_listy_wartosci', 
                             default=')', null=True, blank=True)
    # co najmniej jedna "wartosc" atrybutu musi byc zleksykalizowana
    need_lexicalized_values = BooleanField(db_column='potrzebuje_zleksykalizowych_wartosci', 
                                           default=False)
    # dla atrybutow typu text, okresla, ze wartoscia lematu może byc liczba
    value_can_be_number = BooleanField(db_column='moze_byc_liczba', default=False)
    # priorytet prezentowania atrybutow w argumencie
    priority = PositiveIntegerField(db_column='priorytet', primary_key=True)
    
    def use_subparams(self):
        if (self.possible_parameters.exists() and 
            self.possible_parameters.annotate(subparams_count=Count('possible_subparams')).filter(subparams_count__gt=0).exists()):
            return True
        return False
    
    def __unicode__(self):
      return '%s' % (self.atr_model_name)

# Mozliwe wartosci atrybutow
class Atribute_Value(Model):
    # typ wartosci atrybutu
    type = ForeignKey('AttributeType', db_column='typ', related_name='attribute_values',
                      default=2)
    # wypelniane jesli atrybut jest typu tekstowego
    text = CharField(max_length=32, db_column='tekst', 
                     verbose_name=u'tekst', blank=True, null=True)
    parameter = ForeignKey('AttributeParameter', db_column='parametr', 
                           related_name='attribute_values',
                           blank=True, null=True)
    # argument jesli pole przyjmuje argumenty jako wartosci atrybutu
    argument = ForeignKey('Argument', db_column='argument', related_name='attribute_values',
                          blank=True, null=True)
    # pozycja jesli pole przyjmuje pozycje jako wartosci atrybutu
    position = ForeignKey('Position', db_column='pozycja', related_name='attribute_values',
                          blank=True, null=True)
    # priorytet waznosci atrybutow
    priority = PositiveIntegerField(db_column='priorytet', blank=True, null=True)
    
    def __unicode__(self):
        text_rep = ''
        if self.text:
            text_rep = self.text
        elif self.parameter:
            text_rep = unicode(self.parameter)
        elif self.argument:
            text_rep = self.argument.text_rep
        elif self.position:
            text_rep = self.position.text_rep
        return text_rep
    
def get_or_create_parameter_attr_value(parameter):
    created = False
    parameter_type = AttributeType.objects.get(sym_name='parameter')
    try:
        attr_val_obj = Atribute_Value.objects.get(parameter=parameter,
                                                  type=parameter_type)
    except Atribute_Value.DoesNotExist:
        attr_val_obj = Atribute_Value(parameter=parameter, priority=2000,
                                      type=parameter_type)
        attr_val_obj.save()
        #attr_val_obj.poss.add(*POS.objects.exclude(tag='unk').all())
        created = True
    return attr_val_obj, created

class AttributeParameterModel(Model):
    name = CharField(max_length=24, db_column='nazwa', 
                     verbose_name=u'nazwa')
    sym_name = CharField(max_length=24, db_column='nazwa_symboliczna', 
                         verbose_name=u'nazwa_symboliczna')
    possible_subparams = ManyToManyField('AttributeSubparameter', db_table='mozliwe_podparametry', 
                                    null=True, blank=True, related_name='attr_parameter_models')
    # parametr dostepny tylko w argumentach bedacych realizacjami
    realization_only = BooleanField(db_column='tylko_realizacja', default=False)
    # mozliwe czesci mowy
    poss = ManyToManyField('POS', db_table='czesci_mowy_vs_modele_parametrow',
                           related_name='attr_parameter_models')
    # wartosc dostepna tylko dla dowiazanego modelu parametru
    related = BooleanField(db_column='powiazany', default=False)
    # powiazane modele parametrow
    related_param_models = ManyToManyField('AttributeParameterModel', db_table='powiazane_modele_parametrow', 
                                           null=True, blank=True, related_name='main_param_models')
    # czy model jest aktywny
    active = BooleanField(db_column='aktywny', default=True)
    priority = PositiveIntegerField(db_column='priorytet')
    
    def __unicode__(self):
        return self.name

class AttributeParameter(Model):
    type = ForeignKey('AttributeParameterModel', db_column='typ', 
                      related_name='attribute_parameters')
    subparameters = ManyToManyField('AttributeSubparameter', db_table='podparametry', 
                                    null=True, blank=True, related_name='attribute_parameters')
    priority = PositiveIntegerField(db_column='priorytet')
    
    def __unicode__(self):
        if self.subparameters.exists():
            subparameters_str_tab = [subparam.name for subparam in self.subparameters.order_by('name')]
            text_rep = u'%s[%s]' % (self.type.name, ';'.join(subparameters_str_tab))
        else:
            text_rep = self.type.name
        return text_rep
    
def get_or_create_attr_parameter(model, subparameters):
    created = False
    possible_param_objs = AttributeParameter.objects.annotate(subparams_count=Count('subparameters')).filter(subparams_count=len(subparameters))
    for subparam in subparameters:
        possible_param_objs = possible_param_objs.filter(subparameters=subparam)
    possible_param_objs = possible_param_objs.distinct()
    try:
        param_obj = possible_param_objs.get(type=model)
    except AttributeParameter.DoesNotExist:
        param_obj = AttributeParameter(type=model,
                                       priority=2000)
        param_obj.save()
        param_obj.subparameters.add(*subparameters)
        created = True
    return param_obj, created

class AttributeSubparameter(Model):
    name = CharField(max_length=16, db_column='nazwa', 
                     verbose_name=u'nazwa')
    sym_name = CharField(max_length=16, db_column='nazwa_symboliczna', 
                         verbose_name=u'nazwa_symboliczna')
    opinion = ForeignKey('ArgRealOpinion', 
                         related_name='attr_subparameters')
    priority = PositiveIntegerField(db_column='priorytet')
    
    def __unicode__(self):
        return self.name
############# 
  
class Atribute(Model):
    # wzor/typ atrybutu
    type = CharField(max_length=32, db_column='typ_atrybutu', 
                         verbose_name=u'typ atrybutu') 
    values = ManyToManyField('Atribute_Value', db_table='wartosci', 
                             related_name='attributes')
    # separator (jesli posiada wiecej niz jedna wartosc)
    separator = ForeignKey('AttrValuesSeparator', db_column='separator_wartosci',
                            null=True, blank=True, verbose_name=u'separator wartości',
                            related_name='atributes')
    # tryb wyboru wartosci
    selection_mode = ForeignKey('AttrValueSelectionMode', db_column='tryb_wyboru_wartosci',
                                null=True, blank=True, verbose_name=u'tryb wyboru wartosci',
                                related_name='atributes')

    def sorted_arguments_text_reps(self):
        arguments = [value.argument for value in self.values.all()]
        sorted_arguments = sortArguments(arguments)
        arguments_text_reps = [arg.text_rep for arg in sorted_arguments]
        return arguments_text_reps

    def sorted_positions_text_reps(self):
        positions = [value.position for value in self.values.all()]
        sorted_positions = sortPositions(positions)
        positions_text_reps = [pos['position'].text_rep for pos in sorted_positions]
        return positions_text_reps
    
    def get_values_text_reps(self):
        values_text_reps = []
        attribute_model = Atribute_Model.objects.get(atr_model_name=self.type)
        attribute_type = attribute_model.type.sym_name
        if attribute_type == 'text':
            sorted_values = self.values.order_by('text')
            values_text_reps = [value.text for value in sorted_values]
        elif attribute_type == 'parameter':
            sorted_values = self.values.order_by('parameter__type__priority')
            values_text_reps = [unicode(value.parameter) for value in sorted_values]
        elif attribute_type == 'argument':
            values_text_reps = self.sorted_arguments_text_reps()
        elif attribute_type == 'position':
            values_text_reps = self.sorted_positions_text_reps()
        return values_text_reps
    
    def __unicode__(self):
        text_rep = u''
        values_text_rep = self.get_values_text_reps()
        attr_model = Atribute_Model.objects.get(atr_model_name=self.type)
        if not self.selection_mode:
            text_rep = values_text_rep[0]
        elif self.selection_mode and not self.values.exists():
            text_rep = self.selection_mode.name
        elif self.selection_mode and not self.selection_mode.name and self.values.exists():
            text_rep = self.separator.symbol.join(values_text_rep)
        elif self.selection_mode and self.selection_mode.name and self.values.exists():
            text_rep = u'%s%s%s%s' % (self.selection_mode, 
                                      attr_model.val_list_start,
                                      self.separator.symbol.join(values_text_rep),
                                      attr_model.val_list_end)
        return text_rep
 
def get_or_create_attribute(attribute_model, values, 
                               selection_mode, separator):
    possible_attr_objs = Atribute.objects.annotate(values_count=Count('values')).filter(values_count=len(values))
    for value in values:
        possible_attr_objs = possible_attr_objs.filter(values=value)
    possible_attr_objs = possible_attr_objs.distinct()
    try:
        attr_obj = possible_attr_objs.get(type=attribute_model.atr_model_name,
                                          selection_mode=selection_mode,
                                          separator=separator)
    except Atribute.DoesNotExist:
        attr_obj = Atribute(type=attribute_model.atr_model_name,
                            selection_mode=selection_mode, 
                            separator=separator)
        attr_obj.save()
        attr_obj.values.add(*values)
    return attr_obj
 
     
class Old_Frame(Model):
    # tekst starej ramki zgodnie ze starym slownikiem
    old_frame_value = CharField(max_length=64, db_column='tekst_ramki', 
                                verbose_name=u'tekst ramki',
                                null=True, blank=True) 
    property = ForeignKey('Old_Frame_Property', db_column='wlasciwosc',
                          related_name='old_frames')
    # czy pochodzi od hasla w formie zwrotnej
    reflex = BooleanField(db_column='zwrotna')
    # identyfikator zdania ze skladnicy
    sent_id = TextField(db_column='sent_id', verbose_name=u'identyfikator zdania w skladnicy')
    # argumenty starej ramki
    arguments = ManyToManyField('Skladnica_Argument', db_table='ramka_argumenty',
                                related_name='skladnica_frames', 
                                null=True, blank=True)
    # przyklad
    example = OneToOneField('NKJP_Example', related_name='skladnica_frame',
                            null=True, blank=True)
    # tag okreslajacy czesc mowy
    pos_tag = CharField(max_length=16, db_column='czesc_mowy', 
                        verbose_name=u'część mowy', default='fin') 
    # podramki danej ramki skladnicowej
    sub_frames = ManyToManyField('Old_Frame', db_column='zawarte_ramki',
                                 related_name='skladnica_frames',
                                 null=True, blank=True)
    # czy ramka jest widoczna w interfejsie dodawanie nowych ramek
    visible = BooleanField(db_column='widoczna', default=True) 
    # prawda jesli przyklad nie pasuje do ramki (informacja zwrotna dla skladnicy)
    wrong_example = BooleanField(db_column='bledny_przyklad', default=False)
    
    def __unicode__(self):
        try:
            prepnp = self.arguments.get(text_rep='advp').prepnp.text_rep
            str = '%s: [%s] x %s' % (self.old_frame_value, prepnp, smart_str(self.sub_frames.count()+1))
        except:
            str = '%s: [] x %s' % (self.old_frame_value, smart_str(self.sub_frames.count()+1))
        return str
 
  
class Skladnica_Argument(Model):
    # pomocniczy identyfikator tekstowy argumentu
    text_id = TextField(db_column='text_id', verbose_name=u'identyfikator tekstowy',
                        unique=True)
    # reprezentacja tekstowa w notacji skladnicowej (nie jest unikalna, bo np advp)
    text_rep = TextField(db_column='tekst_rep', verbose_name=u'reprezentacja tekstowa')
    # możliwe argumenty w formacie nowych ramek
    possible_args = ManyToManyField('Arg_Possibility', db_column='mozliwe_argumenty',
                                    related_name='skladnica_args',
                                    null=True, blank=True)
    # fraza przyimkowe (z reguly odpowiedzialna za przeksztalcenie advp w konkretne xp)
    prepnp = ForeignKey('Skladnica_Argument', db_column='prepnp',
                        related_name='skladnica_args', null=True, blank=True)  
    
    def __unicode__(self):
        return u'%s' % (self.text_id)


class Arg_Possibility(Model):
    position_category = ForeignKey('PositionCategory', db_column='kategoria_pozycji', 
                                   related_name='arg_possibilities',
                                   null=True, blank=True)
    argument = ForeignKey('Argument', db_column='argument', 
                          related_name='arg_possibilities',
                          null=True, blank=True)
    
    def __unicode__(self):
        try:
            return u'%s: %s' % (self.position_category.category, self.argument.text_rep)
        except:
            return u'nc: %s' % (self.argument.text_rep)

  
class Old_Frame_Property(Model):
    name = CharField(max_length=16, db_column='nazwa', 
                         verbose_name=u'nazwa', unique=True)
    value = CharField(max_length=16, db_column='pelna_nazwa', 
                         verbose_name=u'pelna_nazwa')
    priority = PositiveIntegerField(db_column='priorytet', blank=True, null=True)
    
    def __unicode__(self):
      return '%s' % (self.name)
    
class Frame_Characteristic(Model):
    # wzor/typ charakterystyki ramki
    type = CharField(max_length=16, db_column='typ_char', 
                         verbose_name=u'typ charakterystyki')
    # wartosc cechy ramki
    value = ForeignKey('Frame_Char_Value', db_column='wartosc_cechy_ramki',
                       related_name='frame_characteristic')
    
    def __unicode__(self):
      return '%s' % (self.value.value)
  
def get_frame_char_and_its_value(pk, none_value):
    char_val = none_value
    if pk:
        try:
            char_obj = Frame_Characteristic.objects.get(pk=pk)
            char_val = char_obj.value.value
        except Frame_Characteristic.DoesNotExist:
            char_obj = None
    else:
        char_obj = None
    return char_obj, char_val

def get_frame_char_by_type_and_value_pk(type, value_pk):
    model = Frame_Char_Model.objects.get(model_name=type)
    value = Frame_Char_Value.objects.get(pk=value_pk)
    char_obj, created = Frame_Characteristic.objects.get_or_create(type=model.model_name, value=value)
    return char_obj
    
class Frame_Char_Model(Model):
    # nazwa typu modelu argumentu
    model_name = CharField(max_length=16, db_column='nazwa_modelu', 
                           verbose_name=u'nazwa modelu', primary_key=True, unique=True)
    # mozliwe wartosci przyjmowane przez dany model charakterystyki ramki
    frame_char_values = ManyToManyField('Frame_Char_Value', db_table='model_wartosciCechRamy',
                                        related_name='frame_char_models')
    # priorytet prezentowania kolejnych klas cech w ramce
    priority = PositiveIntegerField(db_column='priorytet', blank=True, null=True)
    
    def __unicode__(self):
      return '%s' % (self.model_name)
  
def sorted_frame_char_values_dict():
    return {'sorted_reflex_vals': Frame_Char_Model.objects.get(model_name=u'ZWROTNOŚĆ').frame_char_values.order_by('-priority'),
            'sorted_aspect_vals': Frame_Char_Model.objects.get(model_name=u'ASPEKT').frame_char_values.order_by('-priority'),
            'sorted_neg_vals': Frame_Char_Model.objects.get(model_name=u'NEGATYWNOŚĆ').frame_char_values.order_by('-priority'),
            'sorted_pred_vals': Frame_Char_Model.objects.get(model_name=u'PREDYKATYWNOŚĆ').frame_char_values.order_by('-priority')}
    
class Frame_Char_Value(Model):
    value = CharField(max_length=32, db_column='wartosc', 
                         verbose_name=u'wartość cechy ramki', blank=True, null=True)
    # czy wartosc domyslna
    default = BooleanField(db_column='domyslna', default=False)
    # mozliwe czesci mowy
    poss = ManyToManyField('POS', db_table='czesci_mowy',
                           related_name='frame_char_values')
    # priorytet
    priority = PositiveIntegerField(db_column='priorytet', blank=True, null=True)
    
    def __unicode__(self):
      return '%s' % (self.value)
  
def sorted_default_frame_char_vals(): 
    return Frame_Char_Value.objects.filter(default=True).order_by('frame_char_models__priority')
    
class Message(Model):
    # tytul wiadomosci
    topic = TextField(db_column='tytul', verbose_name=u'tytuł')
    # tekst wiadomosci
    message_text = TextField(db_column='wiadomosc', verbose_name=u'wiadomość')
    # uzytkownik pozostawiajacy wiadomosc
    sender = ForeignKey(User, db_column='uzytkownik', related_name='messages')
    # haslo dla ktorego pozostawiono wiadomosc
    lemma = ForeignKey(Lemma, db_column='postac_hasla', related_name='messages',
                       blank=True, null=True)    
    # odbiorca wiadomosci
    recipient = ForeignKey(User, db_column='odbiorca', related_name='recived_messages',
                           blank=True, null=True)
    #data nadania wiadomosci
    time = DateTimeField(auto_now_add=True, db_column='data_nadania') # tutaj, zmieniono z auto_now na auto_now_add
    # czy wiadomosc jest prywatna
    private = BooleanField(db_column='prywatna', default=False)
    # czy wiadomosc jeszcze nie byla czytana
    new = BooleanField(db_column='nowa', default=True) 
    
    class Meta:
      db_table = 'wiadomosci'
      permissions = (
        ('add_notes', u'Może dodawać wiadomości'),
        ('view_notes', u'Może przeglądać swoje notatki')
      )
    
    def __unicode__(self):
      return '%s' % (self.message_text)

class Change(Model):
    # uzytkownik ktory dokonal zmiany
    user = ForeignKey( User, db_column='modyfikujacy', related_name='changes')
    # data modyfikacji
    time = DateTimeField(auto_now_add=True, db_column='data_modyfikacji')
    # stara wersja hasla
    entry = ForeignKey(Lemma, db_column='postac_hasla', related_name='changes',
                       blank=True, null=True)
    # wlasciciel hasla w czasie modyfikacji
    act_owner = ForeignKey(User, db_column='aktualny_wlasciciel', related_name='changesOwn',
                            blank=True, null=True)
    
    class Meta:
      permissions = (
        ('change_lemma_version', u'Może zmieniać wersję hasła'),
      )
    
    def __unicode__(self):
        return '%s data: %s' % (self.entry.entry, self.time)


######################## relacje aspektowe ############################# 
class AspectRelationsGroup(Model):
    # czlonkowie relacji aspektowej
    members = ManyToManyField('Entry', db_table='czlonkowie', 
                              null=True, blank=True, 
                              related_name='aspect_relations_group')
    
    def __unicode__(self):
        str_ls = []
        for entry in self.members.all():
            str_ls.append(entry.name) 
        return ','.join(str_ls)
    
class Entry(Model):
    name = CharField(max_length=64, db_column='nazwa') # usunieto , unique=True
    # czesc mowy
    pos = ForeignKey('POS', db_column='czesc_mowy', related_name='entries')
    # powiazane lematy
    rel_entries = ManyToManyField('Entry', db_table='powiazane_hasla', 
                                  null=True, blank=True, 
                                  related_name='+')
    synonyms = ManyToManyField('Entry', db_table='synonimy', 
                               null=True, blank=True, 
                               related_name='rel_synonyms')
    antonyms = ManyToManyField('Entry', db_table='antonimy', 
                               null=True, blank=True, 
                               related_name='rel_antonyms')
    xcp_examples = ManyToManyField('XcpExample', db_table='przyklady_xcp', 
                                   null=True, blank=True, 
                                   related_name='entries')
    phraseologic_propositions = ManyToManyField('Frame', db_table='propozycje_frazeologiczne', 
                                                blank=True, null=True,
                                                related_name='entries')
    
    class Meta:
        unique_together = ('name', 'pos',)
        permissions = (
            ('change_semantics', u'Może edytować semantykę.'),
            ('view_semantics', u'Może oglądać semantykę.'),
        )
    
    def actual_frames(self):
        # frame_ids = []
        # lexical_units = self.meanings.order_by('sense')
        # for lexical_unit in lexical_units:
        #     frame_ids.extend([f.id for f in lexical_unit.actual_frames()])
        # return get_model('semantics', 'SemanticFrame').objects.filter(id__in=list(set(frame_ids)))
        return self.semantic_frames.filter(next__isnull=True, removed=False)
    
    def matching_connections(self, schema, position, phrase_type):
        frames = self.actual_frames()
        matching_connections = []
        for frame in frames:
            for compl in frame.complements.all():
                matching_realizations = compl.realizations.filter(frame=schema, 
                                                                  position=position, 
                                                                  argument=phrase_type)
                if matching_realizations.exists():
                    realizations_ids = [real.id for real in matching_realizations.all()]
                    matching_connections.append({'compl': compl.id,
                                                 'realizations': realizations_ids})
        return matching_connections
    
    def actual_lemma(self):
        return self.lemmas.get(old=False)
    
    def __unicode__(self):
        return self.name
    
def get_or_create_entry(name, pos):
    created = False
    try:
        entry = Entry.objects.get(name=name, pos__tag='unk')
        entry.pos = pos
        entry.save()
    except Entry.DoesNotExist:
        entry, created = Entry.objects.get_or_create(name=name, pos=pos)
    return entry, created
    
class POS(Model):
    tag = CharField(max_length=16, db_column='tag', unique=True)
    name = CharField(max_length=16, db_column='nazwa')
    # liczba osob potrzebnych do zatwierdzania przykladu wlasnego dla danej czesci mowy
    example_approvers_num = PositiveIntegerField(db_column='liczba_zatwierdzajacych_przyklad', 
                                                 default=2)
    priority = PositiveIntegerField(db_column='priorytet')
    
    def __unicode__(self):
        return self.name
    
def pos_compatible(from_pos, to_pos):
    compatible = False
    if from_pos == to_pos:
        compatible = True
    elif ((from_pos.tag == 'noun' and to_pos.tag == 'adj') or
          (from_pos.tag == 'adj' and to_pos.tag == 'noun')):
        compatible = True
    return compatible
    
class XcpExample(Model):
    arg_regex = CharField(max_length=64, db_column='wzor_argumentu')
    #examples = ManyToManyField('NKJP_Example', related_name='xcp_examples')
    example = ForeignKey('NKJP_Example', related_name='xcp_examples')
    
    def __unicode__(self):
        arg_str = self.arg_regex.replace('.*', '_').replace('\\', '')[1:-1]
        return '%s: %s' % (arg_str, self.example.sentence)

class AttributeType(Model):
    name = CharField(max_length=32, db_column='nazwa', 
                     verbose_name=u'nazwa')
    sym_name = CharField(max_length=32, db_column='nazwa_symboliczna', 
                         verbose_name=u'nazwa_symboliczna')
    priority = PositiveIntegerField(db_column='priorytet')
    
    def __unicode__(self):
        return u'%s' % (self.name)
    
class AttrValueSelectionMode(Model):
    name = CharField(max_length=32, db_column='nazwa', 
                     verbose_name=u'nazwa', blank=True, null=True)
    sym_name = CharField(max_length=32, db_column='nazwa_symboliczna', 
                         verbose_name=u'nazwa_symboliczna')
    # czy nie posiada listy wartosci
    always_empty = BooleanField(db_column='zawsze_pusty', default=False)
    # nie musi posiadac listy wartosci
    can_be_empty = BooleanField(db_column='moze_byc_pusty', default=True)
    priority = PositiveIntegerField(db_column='priorytet')
    
    def __unicode__(self):
        return u'%s' % (self.name)
    
class AttrValuesSeparator(Model):
    symbol = CharField(max_length=8, db_column='symbol', 
                     verbose_name=u'symbol')
    priority = PositiveIntegerField(db_column='priorytet')
    
    def __unicode__(self):
        return u'%s' % (self.symbol)
    
##################### morfeusz exceptions ############################
    
class MorfeuszException(Model):
    orth = CharField(max_length=32, db_column='orth', unique=True)
    interpretations = ManyToManyField('Interpretation', db_table='interpretacje',
                                      related_name='morfeusz_exceptions')
    
    def __unicode__(self):
        interpretations_text_reps = [unicode(interp) for interp in self.interpretations.all()]
        return u'%s[%s]' % (self.orth, u','.join(interpretations_text_reps))
    
class Interpretation(Model):
    pos_tag = ForeignKey('NkjpPosTag', related_name='interpretations',
                         db_column='czesc_mowy')
    base = CharField(max_length=32, db_column='base')
    
    def __unicode__(self):
        return u'%s:%s' % (self.base, self.pos_tag.name)

class NkjpPosTag(Model):
    name = CharField(max_length=16, db_column='nazwa')
    
    def __unicode__(self):
        return u'%s' % (self.name)
    
def is_morfeusz_exception(word, possible_pos_tags):
    is_exception = False
    try:
        interpretations = MorfeuszException.objects.get(orth=word).interpretations
        for interp in interpretations.all():
            if interp.base == word and possible_pos_tags.filter(name=interp.pos_tag.name).exists():
                is_exception = True
                break
    except MorfeuszException.DoesNotExist:
        pass
    return is_exception
    
    
################################## sorting functions #######################################
# sort frame_characteristic names when presented as table of strings
def sortFrameChars(frameChars):
  sorted_frame_char_models = Frame_Char_Model.objects.order_by('priority')
  sorted_frame_chars = []
  for frame_char_model in sorted_frame_char_models:
    for frame_char in frameChars:
      if frame_char_model.model_name == frame_char.type:
        sorted_frame_chars.append(frame_char)       
  return sorted_frame_chars  

def sortatributes(argument):
    sortedatributes = []
    atribute_models = get_attribute_models(argument)
    for atribute_model in atribute_models:
        # warunek, zeby fixed stare dalo sie edytowac
        if argument.atributes.filter(type=atribute_model.atr_model_name).exists():
            sortedatributes.append(argument.atributes.get(type=atribute_model.atr_model_name))
    return sortedatributes  

def get_attribute_models(argument):
    arg_model = Argument_Model.objects.get(arg_model_name=argument.type)
    attribute_models = arg_model.atribute_models.order_by('priority')
    if (attribute_models.filter(atr_model_name=u'TYP FRAZY').exists() and
        argument.atributes.filter(type=u'TYP FRAZY').exists()): 
        
        # drugi warunek zeby dalo sie edytowac stare fixy
        arg_attr = argument.atributes.get(type=u'TYP FRAZY').values.all()[0].argument
        #argument_attr_model = Argument_Model.objects.get(arg_model_name=arg_attr.type)
        #attr_models_to_exclude = argument_attr_model.sec_attr_limitations.all()
        attr_models_to_exclude = get_attr_models_to_exclude(arg_attr)
        
        attribute_models = attribute_models.exclude(pk__in=attr_models_to_exclude)
        attribute_models = attribute_models.order_by('priority')
    return attribute_models

#####################################################  
def get_attr_models_to_exclude(argument):
    argument_model = Argument_Model.objects.get(arg_model_name=argument.type)
    if argument_model.use_subarg_for_limitations:
        sub_arg_model_name = argument.atributes.all()[0].values.all()[0].argument.type
        argument_model = Argument_Model.objects.get(arg_model_name=sub_arg_model_name)
    return argument_model.sec_attr_limitations.all()
#####################################################

def sortArguments(list):
  ret_list = []
  typed_list = []
  models = Argument_Model.objects.all().order_by('priority')
  for model in models:
    typed_list.append([])
    for elem in list:
      if(elem.type == model.arg_model_name):
        typed_list[len(typed_list)-1].append(elem.text_rep) 
  for ls in typed_list:
    ls.sort()
  for ls in typed_list:
    for text_rp in ls:
      for elem in list:
        if(elem.text_rep == text_rp):
          ret_list.append(elem)
          break    
  return ret_list 
  
def sortPositions(positions):
  ret_list = []
  pos_dict_list = []
  # sortowanie argumentow na pozycjach
  for position in positions:
    sortedArguments = sortArguments(position.arguments.all())
    position_dict = {"position": position,
                     "arguments": sortedArguments}
    pos_dict_list.append(position_dict)      
  # sortowanie pozycji wedlug kategorii
  sorted_pos_cats = PositionCategory.objects.order_by("priority")
  pos_to_sort = []
  
  for pos_cat in sorted_pos_cats:
    same_cat_positions = []
    first_pos_arg_list = []
    for position in pos_dict_list:
      if len(position['position'].categories.all()) > 0:
        first_prior_cat = position["position"].categories.order_by("priority")[0]
        if first_prior_cat == pos_cat:
          same_cat_positions.append(position)
    
    while len(same_cat_positions) != 0:
      highestPriorFrame = getHighestPriorFrame(same_cat_positions, 0)
      ret_list.append(highestPriorFrame)
      same_cat_positions.remove(highestPriorFrame)

  blanc_cat_positions = []  
  for position in pos_dict_list:
    if len(position["position"].categories.all()) == 0:
      blanc_cat_positions.append(position)
      
  while len(blanc_cat_positions) != 0:
    highestPriorFrame = getHighestPriorFrame(blanc_cat_positions, 0)
    ret_list.append(highestPriorFrame)
    blanc_cat_positions.remove(highestPriorFrame)
    
  return ret_list
  
# sort categories names when presented as table of strings
def sortPosCatsAsStrTab(pos_cat_str_tab):
  sorted_pos_cat_objs = PositionCategory.objects.order_by('priority')
  sorted_cats = []
  for pos_cat_obj in sorted_pos_cat_objs:
    for pos_cat_str in pos_cat_str_tab:
      if pos_cat_str == pos_cat_obj.category:
        sorted_cats.append(pos_cat_str)
  return sorted_cats 

# wyszukuje ramke o najwyzszym priorytecie
# pomija rozdzileanie kategorii
def getHighestPriorFrame(positions_dict_list, row_idx):
  arguments = []

  for position in positions_dict_list:
    if(row_idx < len(position['arguments'])):
      arguments.append(position['arguments'][row_idx])
      
  sorted_args = sortArguments(arguments) 
  same_args = 0
  
  for arg in sorted_args:
    if arg == sorted_args[0]:
      same_args = same_args + 1
      
  if same_args > 1:
    same_args_dict_list = []
    for position_dict in positions_dict_list:
      if position_dict['arguments'][row_idx] == sorted_args[0]:
        same_args_dict_list.append(position_dict)
    
    # sprawdz czy wszystkie pozycje nie sa takie same
    positionsSame = True
    for same_arg_pos in same_args_dict_list: 
      if len(same_arg_pos['arguments']) > row_idx+1:
        positionsSame = False 
    if positionsSame:
      return same_args_dict_list[0]
    else:  
      return getHighestPriorFrame(same_args_dict_list, row_idx+1)
   
  for position_dict in positions_dict_list:
    if len(position_dict['arguments']) <= row_idx:
      return position_dict
    elif position_dict['arguments'][row_idx]==sorted_args[0]: 
      return position_dict
    
