models.py 6.46 KB
# -*- coding: utf-8 -*-
from itertools import izip

from django.db.models import Model, CharField, TextField, ForeignKey, \
    ManyToManyField, BooleanField

from dictionary.models import PartOfSpeech, LexemeAttribute, \
    LexemeAttributeValue
from patterns.models import PatternType
from tables.util import prepare_table


class Variant(Model):
    TYPE_TABLE = 'table'
    TYPE_EXPORT = 'export'
    TYPE_CHOICES = (
        (TYPE_TABLE, 'tabelka'),
        (TYPE_EXPORT, 'eksport'),
    )

    id = CharField(max_length=32, primary_key=True, db_column='wariant')
    type = CharField(max_length=10, choices=TYPE_CHOICES)

    def __unicode__(self):
        return self.id

    class Meta:
        db_table = 'warianty'


class TableTemplate(Model):
    name = TextField()
    variant = ForeignKey(Variant)
    parts_of_speech = ManyToManyField(PartOfSpeech)
    pattern_types = ManyToManyField(PatternType)
    attributes = ManyToManyField(LexemeAttribute)
    attribute_values = ManyToManyField(LexemeAttributeValue)
    cell_attributes = ManyToManyField(LexemeAttribute, related_name='templates')
    takes_gender = BooleanField(default=False)

    def filter_cells_attr(self, cells, attr_vals):
        for attr in self.cell_attributes.all():
            attr_val = [a for a in attr_vals if a.attribute == attr]
            if len(attr_val) != 1:
                return []
            cells = cells.filter(attribute_values=attr_val[0])
        return cells

    def filter_cells(self, pattern_type, gender, attr_vals):
        if self.variant.type == Variant.TYPE_TABLE:
            cells = self.table_cells.filter(
                pattern_types=pattern_type).select_related('base_form_label')
        else:
            cells = self.export_cells.filter(
                pattern_types=pattern_type).select_related('base_form_label')
        if self.takes_gender and gender:
            cells = cells.filter(genders=gender)
        if attr_vals:
            cells = self.filter_cells_attr(cells, attr_vals)
        return cells

    def filter_table_headers(self, pattern_type, gender, attr_vals):
        headers = self.headers.filter(pattern_types=pattern_type)
        if self.takes_gender and gender:
            headers = headers.filter(genders=gender)
        if attr_vals:
            headers = self.filter_cells_attr(headers, attr_vals)
        return headers

    def render_with_pattern(self, pattern, *args, **kwargs):
        base_endings = pattern.base_endings()
        return self.render(
            pattern.type, *args, pattern=pattern, base_endings=base_endings,
            **kwargs)

    def render_with_pattern_type(self, pattern_type, *args, **kwargs):
        base_endings = pattern_type.dummy_base_endings()
        return prepare_table(self.render(
            pattern_type, *args, base_endings=base_endings, numbers=True,
            **kwargs))

    def render(self, pattern_type, gender, attr_vals, separated=False,
               numbers=False, pattern=None, **forms_kwargs):
        table_cells = self.filter_cells(pattern_type, gender, attr_vals)
        headers = self.filter_table_headers(pattern_type, gender, attr_vals)
        if table_cells is None or headers is None:
            return []
        rows = set()
        last_col = 0
        for table_cell in table_cells:
            rows.add(table_cell.row)
            col = table_cell.col + table_cell.colspan - 1
            if col > last_col:
                last_col = col
        for header in headers:
            rows.add(header.row)
            col = header.col + header.colspan - 1
            if col > last_col:
                last_col = col
        if numbers:
            last_col += 1
        table = [[{'type': 'empty'} for i in xrange(last_col)] for row in rows]
        rows = sorted(rows)
        if numbers:
            for row, table_row in izip(rows, table):
                table_row[0] = {
                    'type': 'header',
                    'label': [row],
                    'css_class': 'blank',
                    'rowspan': 1,
                    'colspan': 1}
        # słownik: nr rzędu w bazie -> rzeczywisty numer rzędu
        row_translate = dict((r, i) for i, r in enumerate(rows))
        for tc in table_cells:
            if numbers:
                x = tc.col
            else:
                x = tc.col - 1
            y = row_translate[tc.row]
            table_cell = table[y][x]
            assert table_cell['type'] != 'span'
            separator = u'·' if separated else u''
            forms = [f + (pattern,)
                     for f in tc.forms(separator=separator, **forms_kwargs)]
            if not forms:
                continue
            if table_cell['type'] == 'empty':
                table[y][x] = {
                    'type': 'forms',
                    'forms': forms,
                    'rowspan': tc.rowspan,
                    'colspan': tc.colspan,
                }
                for i in xrange(tc.colspan):
                    for j in xrange(tc.rowspan):
                        if (i, j) != (0, 0):
                            assert table[y + j][x + i]['type'] == 'empty'
                            table[y + j][x + i]['type'] = 'span'
            else:
                assert tc.rowspan == table_cell['rowspan']
                assert tc.colspan == table_cell['colspan']
                table_cell['forms'] += forms
        for header in headers:
            if numbers:
                x = header.col
            else:
                x = header.col - 1
            y = row_translate[header.row]
            assert table[y][x]['type'] == 'empty'
            table[y][x] = {
                'type': 'label',
                'label': [header.label],
                'css_class': header.css_class,
                'rowspan': header.rowspan,
                'colspan': header.colspan,
            }
            for i in xrange(header.colspan):
                for j in xrange(header.rowspan):
                    if (i, j) != (0, 0):
                        assert table[y + j][x + i]['type'] == 'empty'
                        table[y + j][x + i]['type'] = 'span'
        if numbers:
            first_row = [{
                'type': 'header',
                'label': [col if col != 0 else ''],
                'css_class': 'blank',
                'rowspan': 1,
                'colspan': 1} for col in xrange(last_col)]
            table = [first_row] + table
        return [row for row in table
                if not all(cell['type'] == 'empty' for cell in row)]

    def __unicode__(self):
        return self.name