diff --git a/dictionary/management/commands/cache_form_qualifiers.py b/dictionary/management/commands/cache_form_qualifiers.py index c619a0b..d034aae 100644 --- a/dictionary/management/commands/cache_form_qualifiers.py +++ b/dictionary/management/commands/cache_form_qualifiers.py @@ -7,7 +7,7 @@ from django.core.management.base import BaseCommand from common.util import uniprint from export.lexeme_form_query import WHERE_CLAUSES, \ attr_clauses_combinations, TABLE_FROM_CLAUSES -from dictionary.models import TableTemplate +from tables.models import TableTemplate class Command(BaseCommand): diff --git a/dictionary/management/commands/create_forms.py b/dictionary/management/commands/create_forms.py index 200f082..6f7e9a8 100644 --- a/dictionary/management/commands/create_forms.py +++ b/dictionary/management/commands/create_forms.py @@ -7,7 +7,8 @@ from django.db.transaction import atomic from common.util import uniprint from export.lexeme_form_query import WHERE_CLAUSES, \ attr_clauses_combinations, TABLE_FROM_CLAUSES -from dictionary.models import TableTemplate, Lexeme, LexemeForm +from dictionary.models import Lexeme, LexemeForm +from tables.models import TableTemplate class Command(BaseCommand): diff --git a/dictionary/management/commands/stale/convert_tables.py b/dictionary/management/commands/stale/convert_tables.py index 7b9e943..11fc523 100644 --- a/dictionary/management/commands/stale/convert_tables.py +++ b/dictionary/management/commands/stale/convert_tables.py @@ -3,7 +3,7 @@ from django.core.management import BaseCommand from common.util import GroupDict from dictionary.models import Cell, TableTemplate, PatternType, TableTemplate, TableCell, TableHeader, \ TableHeader, LexemeAttribute, ExportCell, InflectionCharacteristic, LexemeAttributeValue -from tables.models import Variant +from tables.models import Variant, TableTemplate from patterns.models import InflectionType, PatternType diff --git a/dictionary/management/commands/stale/import_variant.py b/dictionary/management/commands/stale/import_variant.py index d918003..2d7e7c4 100644 --- a/dictionary/management/commands/stale/import_variant.py +++ b/dictionary/management/commands/stale/import_variant.py @@ -5,7 +5,7 @@ from django.core.management.base import BaseCommand from common.util import uniopen from dictionary.models import TableTemplate, Cell, TableCell, PartOfSpeech, \ PatternType, InflectionCharacteristic -from tables.models import Variant +from tables.models import Variant, TableTemplate from patterns.models import BaseFormLabel, PatternType diff --git a/dictionary/migrations/0014_auto_20151213_1328.py b/dictionary/migrations/0014_auto_20151213_1328.py new file mode 100644 index 0000000..6ddfd4a --- /dev/null +++ b/dictionary/migrations/0014_auto_20151213_1328.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dictionary', '0013_auto_20151213_1303'), + ] + + database_operations = [ + migrations.AlterModelTable('tabletemplate', 'tables_tabletemplate'), + ] + + operations = [ + migrations.SeparateDatabaseAndState( + database_operations=database_operations), + ] diff --git a/dictionary/migrations/0015_auto_20151213_1328.py b/dictionary/migrations/0015_auto_20151213_1328.py new file mode 100644 index 0000000..1461a8e --- /dev/null +++ b/dictionary/migrations/0015_auto_20151213_1328.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dictionary', '0014_auto_20151213_1328'), + ('tables', '0002_tabletemplate'), + ] + + state_operations = [ + migrations.RemoveField( + model_name='tabletemplate', + name='attribute_values', + ), + migrations.RemoveField( + model_name='tabletemplate', + name='attributes', + ), + migrations.RemoveField( + model_name='tabletemplate', + name='cell_attributes', + ), + migrations.RemoveField( + model_name='tabletemplate', + name='parts_of_speech', + ), + migrations.RemoveField( + model_name='tabletemplate', + name='pattern_types', + ), + migrations.RemoveField( + model_name='tabletemplate', + name='variant', + ), + migrations.DeleteModel( + name='TableTemplate', + ), + ] + + operations = [ + migrations.AlterField( + model_name='exportcell', + name='table_template', + field=models.ForeignKey(related_name='export_cells', to='tables.TableTemplate'), + ), + migrations.AlterField( + model_name='tablecell', + name='table_template', + field=models.ForeignKey(related_name='table_cells', to='tables.TableTemplate'), + ), + migrations.AlterField( + model_name='tableheader', + name='table_template', + field=models.ForeignKey(related_name='headers', to='tables.TableTemplate'), + ), + migrations.SeparateDatabaseAndState(state_operations=state_operations), + ] diff --git a/dictionary/models.py b/dictionary/models.py index 9b878c9..c16921f 100644 --- a/dictionary/models.py +++ b/dictionary/models.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import json from collections import OrderedDict -from itertools import izip from django.conf import settings from django.contrib.auth.models import User, Permission, AnonymousUser @@ -13,7 +12,6 @@ from accounts.util import users_with_perm from common.models import NotDeletedManager from common.util import no_history from patterns.models import InflectionType, BaseFormLabel, PatternType, Pattern -from tables.models import Variant from tables.util import prepare_table @@ -709,6 +707,7 @@ class LexemeInflectionPattern(Model): def table_template(self, variant, attr_vals=None, pos=None): part_of_speech = pos or self.lexeme.part_of_speech + from tables.models import TableTemplate tts = TableTemplate.objects.filter( parts_of_speech=part_of_speech, variant=variant, pattern_types=self.pattern.type) @@ -1010,152 +1009,6 @@ class CrossReference(Model): db_table = 'odsylacze' -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 - - class Cell(Model): base_form_label = ForeignKey(BaseFormLabel) prefix = CharField(max_length=20, blank=True) @@ -1227,7 +1080,7 @@ class Cell(Model): class TableCell(Cell): - table_template = ForeignKey(TableTemplate, related_name='table_cells') + table_template = ForeignKey('tables.TableTemplate', related_name='table_cells') pattern_types = ManyToManyField(PatternType) genders = ManyToManyField(Gender) attribute_values = ManyToManyField( @@ -1258,7 +1111,7 @@ class TableCell(Cell): class ExportCell(Cell): - table_template = ForeignKey(TableTemplate, related_name='export_cells') + table_template = ForeignKey('tables.TableTemplate', related_name='export_cells') pattern_types = ManyToManyField(PatternType) genders = ManyToManyField(Gender) attribute_values = ManyToManyField(LexemeAttributeValue) @@ -1272,7 +1125,7 @@ class ExportCell(Cell): class TableHeader(Model): - table_template = ForeignKey(TableTemplate, related_name='headers') + table_template = ForeignKey('tables.TableTemplate', related_name='headers') pattern_types = ManyToManyField(PatternType) genders = ManyToManyField(Gender) attribute_values = ManyToManyField(LexemeAttributeValue) @@ -1332,7 +1185,7 @@ class HomonymNumber(Model): for pos in poses) lexeme = ForeignKey(Lexeme) - variant = ForeignKey(Variant) + variant = ForeignKey('tables.Variant') number = IntegerField() objects = LexemeNotDeletedManager() diff --git a/export/lexeme_export.py b/export/lexeme_export.py index e266a39..f6bfd5c 100644 --- a/export/lexeme_export.py +++ b/export/lexeme_export.py @@ -11,9 +11,9 @@ from common.util import debug, flatten, uniprint from export.lexeme_form_query import attr_clauses_combinations, \ EXPORT_FROM_CLAUSES, WHERE_CLAUSES from dictionary.models import CrossReferenceType, ClassificationValue, \ - LexemeAttributeValue, Gender, TableTemplate, HomonymNumber, Lexeme, \ + LexemeAttributeValue, Gender, HomonymNumber, Lexeme, \ LexemeAttribute, SavedExportData - +from tables.models import TableTemplate locale.setlocale(locale.LC_ALL, 'pl_PL.utf8') diff --git a/management/ajax_table_view.py b/management/ajax_table_view.py index a370f61..e7c94b0 100644 --- a/management/ajax_table_view.py +++ b/management/ajax_table_view.py @@ -5,9 +5,9 @@ from dictionary.forms import LexemeClosedAttributeForm from management.forms import CellRestrictionsForm, GenderForm, \ AttributeValuesForm, BaseFormLabelForm, CSSClassForm, TemplatePreviewForm, \ TemplatePreviewGenderForm -from dictionary.models import TableTemplate, \ - TableCell, TableHeader, ExportCell, Gender, LexemeAttributeValue -from tables.models import Variant +from dictionary.models import TableCell, TableHeader, ExportCell, Gender, \ + LexemeAttributeValue +from tables.models import Variant, TableTemplate from patterns.models import BaseFormLabel, PatternType diff --git a/management/forms.py b/management/forms.py index 07e6c58..8660d6b 100644 --- a/management/forms.py +++ b/management/forms.py @@ -10,7 +10,8 @@ from common.models import FlatPage from common.util import GroupDict from dictionary.models import Classification, ClassificationValue, Qualifier, \ QualifierExclusionClass, Lexeme, LexemeInflectionPattern, Vocabulary, \ - TableTemplate, Gender, LexemeAttributeValue, MultilingualText + Gender, LexemeAttributeValue, MultilingualText +from tables.models import TableTemplate from patterns.models import BaseFormLabel, PatternType, Ending CSS_CLASS_CHOICES = ( diff --git a/tables/management/commands/export_template.py b/tables/management/commands/export_template.py index 1c35f35..4e527b5 100644 --- a/tables/management/commands/export_template.py +++ b/tables/management/commands/export_template.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- from django.core.management.base import BaseCommand from common.util import json_encode, uniprint -from dictionary.models import TableTemplate -from tables.models import Variant +from tables.models import Variant, TableTemplate class Command(BaseCommand): diff --git a/tables/management/commands/export_templates.py b/tables/management/commands/export_templates.py index cf82ac4..e9115eb 100644 --- a/tables/management/commands/export_templates.py +++ b/tables/management/commands/export_templates.py @@ -2,7 +2,7 @@ from django.core.management.base import BaseCommand from common.util import uniprint, json_encode -from dictionary.models import TableTemplate +from tables.models import TableTemplate from tables.management.commands.export_template import export_template diff --git a/tables/management/commands/import_template.py b/tables/management/commands/import_template.py index b6cf9a9..1bb4ad4 100644 --- a/tables/management/commands/import_template.py +++ b/tables/management/commands/import_template.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- import json from django.core.management.base import BaseCommand -from dictionary.models import TableTemplate, TableCell, \ - PatternType, LexemeAttributeValue, TableHeader, ExportCell, Gender -from tables.models import Variant +from dictionary.models import TableCell, \ + LexemeAttributeValue, TableHeader, ExportCell, Gender +from tables.models import Variant, TableTemplate from patterns.models import BaseFormLabel, PatternType diff --git a/tables/management/commands/import_templates.py b/tables/management/commands/import_templates.py index 6be9c5c..f4b1b4a 100644 --- a/tables/management/commands/import_templates.py +++ b/tables/management/commands/import_templates.py @@ -3,8 +3,8 @@ import json from django.core.management.base import BaseCommand -from dictionary.models import TableTemplate, LexemeAttribute, \ - LexemeAttributeValue +from dictionary.models import LexemeAttribute, LexemeAttributeValue +from tables.models import TableTemplate from patterns.models import PatternType from tables.management.commands.import_template import import_template diff --git a/tables/migrations/0002_tabletemplate.py b/tables/migrations/0002_tabletemplate.py new file mode 100644 index 0000000..92988ae --- /dev/null +++ b/tables/migrations/0002_tabletemplate.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('patterns', '0006_auto_20151213_1153'), + ('dictionary', '0014_auto_20151213_1328'), + ('tables', '0001_initial'), + ] + + state_operations = [ + migrations.CreateModel( + name='TableTemplate', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('name', models.TextField()), + ('takes_gender', models.BooleanField(default=False)), + ('attribute_values', models.ManyToManyField(to='dictionary.LexemeAttributeValue')), + ('attributes', models.ManyToManyField(to='dictionary.LexemeAttribute')), + ('cell_attributes', models.ManyToManyField(related_name='templates', to='dictionary.LexemeAttribute')), + ('parts_of_speech', models.ManyToManyField(to='dictionary.PartOfSpeech')), + ('pattern_types', models.ManyToManyField(to='patterns.PatternType')), + ('variant', models.ForeignKey(to='tables.Variant')), + ], + ), + ] + + operations = [ + migrations.SeparateDatabaseAndState(state_operations=state_operations) + ] diff --git a/tables/models.py b/tables/models.py index 5b621e4..f1f79da 100644 --- a/tables/models.py +++ b/tables/models.py @@ -1,5 +1,13 @@ # -*- coding: utf-8 -*- -from django.db.models import Model, CharField +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): @@ -18,3 +26,149 @@ class Variant(Model): 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 \ No newline at end of file