Commit ebddb9db0e9e0a54835e56673b8ba10c22b14c66
Merge branch 'master' of ssh://git.nlp.ipipan.waw.pl:8888/walenty/ShellValier
Showing
31 changed files
with
2621 additions
and
558 deletions
common/decorators.py
0 → 100644
1 | +from django.http import HttpResponseBadRequest | |
2 | + | |
3 | +#https://djangosnippets.org/snippets/771/ | |
4 | +def ajax_required(f): | |
5 | + def wrap(request, *args, **kwargs): | |
6 | + if not request.is_ajax(): | |
7 | + return HttpResponseBadRequest() | |
8 | + return f(request, *args, **kwargs) | |
9 | + wrap.__doc__=f.__doc__ | |
10 | + wrap.__name__=f.__name__ | |
11 | + return wrap | |
... | ... |
common/static/common/css/common.css
0 → 100644
1 | +html, body { | |
2 | + height: 100%; | |
3 | +} | |
4 | + | |
5 | +main { | |
6 | + height: 100%; | |
7 | + overflow: hidden; | |
8 | +} | |
9 | + | |
10 | +.min-100 { | |
11 | + min-width: 100%; | |
12 | +} | |
13 | + | |
14 | +.border-lightgray { | |
15 | + border-color: #b0c4d9; | |
16 | +} | |
17 | + | |
18 | +legend { | |
19 | + font-size: 1.2rem; | |
20 | +} | |
21 | + | |
22 | +.tooltip > .tooltip-inner > ul { | |
23 | + list-style-position: inside; | |
24 | + padding-left: 0; | |
25 | +} | |
26 | + | |
27 | +.tooltip > .tooltip-inner { | |
28 | + text-align: left; | |
29 | + max-width: 500px; | |
30 | +} | |
31 | + | |
32 | +.ui-autocomplete { | |
33 | + z-index: 2000; | |
34 | +} | |
35 | + | |
36 | +.remove-button { | |
37 | + float: right; | |
38 | +} | |
39 | + | |
40 | +.remove-or-button { | |
41 | + float: right; | |
42 | +} | |
43 | + | |
44 | +.subform { | |
45 | + border-style: solid; | |
46 | + border-width: 3px; | |
47 | +} | |
48 | + | |
49 | +.form-depth-1 { | |
50 | + background-color: #f2f2f2; | |
51 | + border-color: #e4e4e4; | |
52 | +} | |
53 | + | |
54 | +.form-depth-2 { | |
55 | + background-color: #e4e4e4; | |
56 | + border-color: #d6d6d6; | |
57 | +} | |
58 | + | |
59 | +.form-depth-3 { | |
60 | + background-color: #d6d6d6; | |
61 | + border-color: #c8c8c8; | |
62 | +} | |
63 | + | |
64 | +.form-depth-4 { | |
65 | + background-color: #c8c8c8; | |
66 | + border-color: #bababa; | |
67 | +} | |
68 | + | |
69 | +.form-depth-5 { | |
70 | + background-color: #bababa; | |
71 | + border-color: #acacac; | |
72 | +} | |
73 | + | |
74 | +.form-depth-6 { | |
75 | + background-color: #acacac; | |
76 | + border-color: #9e9e9e; | |
77 | +} | |
78 | + | |
79 | +.form-depth-7 { | |
80 | + background-color: #9e9e9e; | |
81 | + border-color: #909090; | |
82 | +} | |
83 | + | |
84 | +.form-depth-8 { | |
85 | + background-color: #909090; | |
86 | + border-color: #828282; | |
87 | +} | |
88 | + | |
89 | +.form-depth-9 { | |
90 | + background-color: #828282; | |
91 | + border-color: #747474; | |
92 | +} | |
93 | + | |
94 | +.form-depth-10 { | |
95 | + background-color: #747474; | |
96 | + border-color: #666666; | |
97 | +} | |
98 | + | |
99 | +.bg-lightgray { | |
100 | + background-color: #b0c4d9; | |
101 | +} | |
102 | + | |
103 | +.to-remove { | |
104 | + background-color: #f6ce95; | |
105 | +} | |
106 | + | |
107 | +.form-group { | |
108 | + margin-bottom: 2px; | |
109 | + | |
110 | +} | |
... | ... |
common/static/common/favicon.ico
0 → 100644
No preview for this file type
common/static/common/js/csrf.js
0 → 100644
1 | +"use strict"; | |
2 | + | |
3 | +//https://docs.djangoproject.com/en/2.2/ref/csrf/ | |
4 | + | |
5 | +function getCookie(name) { | |
6 | + var cookieValue = null; | |
7 | + if (document.cookie && document.cookie !== '') { | |
8 | + var cookies = document.cookie.split(';'); | |
9 | + for (var i = 0; i < cookies.length; i++) { | |
10 | + var cookie = cookies[i].trim(); | |
11 | + // Does this cookie string begin with the name we want? | |
12 | + if (cookie.substring(0, name.length + 1) === (name + '=')) { | |
13 | + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); | |
14 | + break; | |
15 | + } | |
16 | + } | |
17 | + } | |
18 | + return cookieValue; | |
19 | +} | |
20 | +var csrftoken = getCookie('csrftoken'); | |
21 | + | |
22 | +function csrfSafeMethod(method) { | |
23 | + // these HTTP methods do not require CSRF protection | |
24 | + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); | |
25 | +} | |
26 | +$.ajaxSetup({ | |
27 | + beforeSend: function(xhr, settings) { | |
28 | + if (!csrfSafeMethod(settings.type) && !this.crossDomain) { | |
29 | + xhr.setRequestHeader("X-CSRFToken", csrftoken); | |
30 | + } | |
31 | + } | |
32 | +}); | |
... | ... |
common/static/common/js/utils.js
0 → 100644
common/templates/base.html
... | ... | @@ -13,7 +13,7 @@ |
13 | 13 | <link rel="stylesheet" type="text/css" href="{% static 'common/css/common.css' %}"> |
14 | 14 | <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> |
15 | 15 | <!--might be needed for resizeable panels, causes errors if included after Popper--> |
16 | - <!--script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script--> | |
16 | + <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script> | |
17 | 17 | <!--Bootstrap’s tooltips require Popper--> |
18 | 18 | <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script> |
19 | 19 | <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> |
... | ... |
common/views.py
... | ... | @@ -2,3 +2,9 @@ from django.shortcuts import render |
2 | 2 | |
3 | 3 | def dash(request): |
4 | 4 | return render(request, 'dash.html') |
5 | + | |
6 | +def error_400(request, exception): | |
7 | + return render(request, 'base.html', status=400) | |
8 | + | |
9 | +def error_404(request, exception): | |
10 | + return render(request, 'base.html', status=404) | |
... | ... |
connections/models.py
... | ... | @@ -3,7 +3,8 @@ from django.db import models |
3 | 3 | from examples.models import Example |
4 | 4 | from meanings.models import LexicalUnit |
5 | 5 | from semantics.models import Argument |
6 | -from syntax.models import PhraseType, Position, Schema, Subentry | |
6 | +from syntax.models import Position, Schema, Subentry | |
7 | +from syntax.models_phrase import PhraseType | |
7 | 8 | |
8 | 9 | |
9 | 10 | class Entry(models.Model): |
... | ... |
entries/autocompletes.py
0 → 100644
1 | +from django.http import JsonResponse | |
2 | + | |
3 | +from syntax.models_phrase import PhraseType, FixedAttributes | |
4 | +from connections.models import Entry | |
5 | +from meanings.models import LexicalUnit | |
6 | + | |
7 | +from entries.form_fields.query_managers import RegexQueryManager | |
8 | + | |
9 | +lookups = { | |
10 | + 'phrasetype' : (PhraseType, 'text_rep'), | |
11 | + 'lemma' : (Entry, 'name'), | |
12 | + 'fixed' : (FixedAttributes, 'text'), | |
13 | + 'lu' : (LexicalUnit, 'text_rep') | |
14 | +} | |
15 | + | |
16 | +def autocomplete(request): | |
17 | + if request.method == 'GET': | |
18 | + what, text = request.GET['what'], request.GET['text'] | |
19 | + text = '{}.*'.format(text.strip()) | |
20 | + suggestions = [] | |
21 | + print(what) | |
22 | + if what in lookups: | |
23 | + model, field = lookups[what] | |
24 | + qm = RegexQueryManager(lookup=field) | |
25 | + try: | |
26 | + queries = qm.make_queries(text, None) | |
27 | + objects = model.objects.all() | |
28 | + for query in queries: | |
29 | + objects = objects.filter(query).distinct() | |
30 | + suggestions = sorted(getattr(o, field) for o in objects) | |
31 | + except Exception as e: | |
32 | + suggestions = ['brak sugestii (błąd) ' + str(e)] | |
33 | + else: | |
34 | + suggestions = ['brak sugestii (nie zdefiniowano)'] | |
35 | + if not suggestions: | |
36 | + suggestions = ['brak sugestii'] | |
37 | + if len(suggestions) > 10: | |
38 | + suggestions = suggestions[:10] + ['+ {} sugestii'.format(len(suggestions) - 10)] | |
39 | + return JsonResponse({'suggestions' : suggestions}) | |
40 | + | |
... | ... |
entries/form_fields/generic_fields.py
... | ... | @@ -14,93 +14,108 @@ from .query_managers import ( |
14 | 14 | ComboQueryManager |
15 | 15 | ) |
16 | 16 | |
17 | + | |
17 | 18 | class LayoutField(object): |
18 | 19 | |
19 | - def layout(self, x): | |
20 | - return x | |
20 | + def layout(self, x, **kwargs): | |
21 | + return layout.Field(x, **kwargs) | |
22 | + | |
21 | 23 | |
22 | 24 | class CheckboxesLayoutField(object): |
23 | 25 | |
24 | - def layout(self, x): | |
25 | - return bootstrap.InlineCheckboxes(x) | |
26 | + def layout(self, x, **kwargs): | |
27 | + return bootstrap.InlineCheckboxes(x, **kwargs) | |
28 | + | |
26 | 29 | |
27 | 30 | class RadiosLayoutField(object): |
28 | 31 | |
29 | - def layout(self, x): | |
30 | - return bootstrap.InlineRadios(x) | |
32 | + def layout(self, x, **kwargs): | |
33 | + return bootstrap.InlineRadios(x, **kwargs) | |
34 | + | |
31 | 35 | |
32 | 36 | class SelectLayoutField(object): |
33 | 37 | |
34 | - def layout(self, x): | |
35 | - return layout.Field(x, css_class='custom-select') | |
38 | + def layout(self, x, **kwargs): | |
39 | + return layout.Field(x, css_class='custom-select', **kwargs) | |
36 | 40 | |
37 | 41 | # ============================================================================== |
38 | 42 | |
43 | + | |
39 | 44 | #TODO move implementation common for RangesFilter and RegexFilter to an abstract ExpressionFilter? |
40 | 45 | class RangeFilter(forms.CharField, LayoutField): |
41 | 46 | |
42 | - def __init__(self, label, entry_lookup, object_lookup, initial='', empty_value='', **kwargs): | |
47 | + def __init__(self, label, lookup, initial='', empty_value='', **kwargs): | |
43 | 48 | super().__init__(label=label, required=False, initial=initial, empty_value=empty_value, **kwargs) |
44 | - self.query_manager = RangesQueryManager(entry_lookup, object_lookup) | |
49 | + self.query_manager = RangesQueryManager(lookup) | |
45 | 50 | |
46 | 51 | # can’t use static default_validators since the query manager is a class field |
47 | 52 | def validate(self, value): |
48 | 53 | self.query_manager.expression_validator(value) |
49 | 54 | |
55 | + | |
50 | 56 | class RegexFilter(forms.CharField, LayoutField): |
51 | 57 | |
52 | - def __init__(self, label, entry_lookup, object_lookup, inner_class=None, outer_lookup=None, additional_operators=False, initial='.*', empty_value='.*', **kwargs): | |
58 | + def __init__(self, label, lookup, additional_operators=False, initial='.*', empty_value='.*', autocomplete=None, **kwargs): | |
59 | + self.css_class = 'regex-autocomplete' if autocomplete else None | |
60 | + self.data_autocomplete = autocomplete if autocomplete else None | |
53 | 61 | super().__init__(label=label, required=False, initial=initial, empty_value=empty_value, **kwargs) |
54 | - self.query_manager = RegexQueryManager(entry_lookup, object_lookup, inner_class=inner_class, outer_lookup=outer_lookup, additional_operators=additional_operators) | |
55 | - | |
62 | + self.query_manager = RegexQueryManager(lookup, additional_operators=additional_operators) | |
63 | + | |
56 | 64 | # can’t use static default_validators since validation depends on the |
57 | 65 | # query_manager instance (allowed operators) |
58 | 66 | def validate(self, value): |
59 | 67 | self.query_manager.expression_validator(value) |
68 | + | |
69 | + def layout(self, x, **kwargs): | |
70 | + return layout.Field(x, css_class=self.css_class, data_autocomplete=self.data_autocomplete, **kwargs) | |
71 | + | |
60 | 72 | |
61 | 73 | class SingleRegexFilter(forms.CharField, LayoutField): |
62 | 74 | |
63 | - def __init__(self, label, entry_lookup, object_lookup, initial='.*', empty_value='.*', **kwargs): | |
75 | + def __init__(self, label, lookup, initial='.*', empty_value='.*', **kwargs): | |
64 | 76 | super().__init__(label=label, required=False, initial=initial, empty_value=empty_value, **kwargs) |
65 | - self.query_manager = SingleRegexQueryManager(entry_lookup, object_lookup) | |
77 | + self.query_manager = SingleRegexQueryManager(lookup) | |
66 | 78 | |
79 | + | |
67 | 80 | class MultipleChoiceFilter(forms.MultipleChoiceField, CheckboxesLayoutField): |
68 | 81 | |
69 | - def __init__(self, label, choices, entry_lookup, object_lookup, **kwargs): | |
82 | + def __init__(self, label, choices, lookup, **kwargs): | |
70 | 83 | super().__init__( |
71 | 84 | label=label, |
72 | 85 | choices=choices, |
73 | 86 | required=False, |
74 | 87 | **kwargs |
75 | 88 | ) |
76 | - self.query_manager = MultiValueQueryManager(entry_lookup, object_lookup, default_conjunction=False) | |
89 | + self.query_manager = MultiValueQueryManager(lookup, default_conjunction=False) | |
77 | 90 | |
91 | + | |
78 | 92 | class ModelMultipleChoiceFilter(forms.ModelMultipleChoiceField, CheckboxesLayoutField): |
79 | 93 | |
80 | - def __init__(self, label, queryset, key, entry_lookup, object_lookup, human_values={}, **kwargs): | |
94 | + def __init__(self, label, queryset, key, lookup, human_values={}, **kwargs): | |
81 | 95 | super().__init__( |
82 | 96 | label=label, |
83 | 97 | queryset=queryset, |
84 | 98 | required=False, |
85 | 99 | **kwargs |
86 | 100 | ) |
87 | - self.query_manager = MultiValueQueryManager(entry_lookup, object_lookup, default_conjunction=False) | |
101 | + self.query_manager = MultiValueQueryManager(lookup, default_conjunction=False) | |
88 | 102 | self.key = key |
89 | 103 | self.human_values = human_values |
90 | 104 | |
91 | 105 | def label_from_instance(self, obj): |
92 | 106 | return self.human_values.get(obj.__dict__[self.key], obj.__dict__[self.key]) |
93 | 107 | |
108 | + | |
94 | 109 | class ModelChoiceFilter(forms.ModelChoiceField, SelectLayoutField): |
95 | 110 | |
96 | - def __init__(self, label, queryset, key, entry_lookup, object_lookup, human_values={}, **kwargs): | |
111 | + def __init__(self, label, queryset, key, lookup, human_values={}, **kwargs): | |
97 | 112 | super().__init__( |
98 | 113 | label=label, |
99 | 114 | queryset=queryset, |
100 | 115 | required=False, |
101 | 116 | **kwargs |
102 | 117 | ) |
103 | - self.query_manager = SingleValueQueryManager(entry_lookup, object_lookup) | |
118 | + self.query_manager = SingleValueQueryManager(lookup) | |
104 | 119 | # str object or callable |
105 | 120 | self.key = key |
106 | 121 | self.human_values = human_values |
... | ... | @@ -120,7 +135,7 @@ class ComboFilter(forms.MultiValueField, LayoutField): |
120 | 135 | def decompress(self, value): |
121 | 136 | return value if value else [None for i in range(len(self.widgets))] |
122 | 137 | |
123 | - def layout(self, x): | |
138 | + def layout(self, x, **kwargs): | |
124 | 139 | attrs = [] |
125 | 140 | for i, field in enumerate(self.fields): |
126 | 141 | cls = ['col-sm-3'] |
... | ... | @@ -133,7 +148,7 @@ class ComboFilter(forms.MultiValueField, LayoutField): |
133 | 148 | elif type(field) == SingleRegexFilter: |
134 | 149 | cls.append('form-control') |
135 | 150 | attrs.append({'class' : ' '.join(cls)}) |
136 | - return layout.MultiWidgetField(x, attrs=attrs) | |
151 | + return layout.MultiWidgetField(x, attrs=attrs, **kwargs) | |
137 | 152 | |
138 | 153 | def __init__(self, label, inner_class, outer_lookup, fields, negation_field=False, **kwargs): |
139 | 154 | neg_fields = [SwitchField('!')] if negation_field else [] |
... | ... | @@ -141,7 +156,7 @@ class ComboFilter(forms.MultiValueField, LayoutField): |
141 | 156 | super().__init__( |
142 | 157 | label=label, |
143 | 158 | fields=all_fields, |
144 | - widget=ComboFilter.ComboWidget(widgets=[field.widget for field in all_fields]), | |
159 | + widget=self.ComboWidget(widgets=[field.widget for field in all_fields]), | |
145 | 160 | required=False, |
146 | 161 | **kwargs |
147 | 162 | ) |
... | ... | @@ -151,6 +166,7 @@ class ComboFilter(forms.MultiValueField, LayoutField): |
151 | 166 | def compress(self, data_list): |
152 | 167 | return data_list |
153 | 168 | |
169 | + | |
154 | 170 | class OperatorField(forms.ChoiceField, RadiosLayoutField): |
155 | 171 | |
156 | 172 | def __init__(self): |
... | ... | @@ -168,10 +184,11 @@ class OperatorField(forms.ChoiceField, RadiosLayoutField): |
168 | 184 | return False |
169 | 185 | return None |
170 | 186 | |
187 | + | |
171 | 188 | class SwitchField(forms.BooleanField, LayoutField): |
172 | 189 | |
173 | 190 | def __init__(self, label): |
174 | 191 | super().__init__(label=label, required=False) |
175 | 192 | |
176 | - def layout(self, x): | |
177 | - return layout.Field(x, wrapper_class='custom-switch') | |
193 | + def layout(self, x, **kwargs): | |
194 | + return layout.Field(x, wrapper_class='custom-switch float-right font-weight-bold', **kwargs) | |
... | ... |
entries/form_fields/query_managers.py
... | ... | @@ -13,33 +13,34 @@ from django.core.exceptions import ValidationError |
13 | 13 | from django.db.models import Q |
14 | 14 | from django.utils.translation import gettext as _ |
15 | 15 | |
16 | +# TODO update | |
16 | 17 | ''' |
17 | -QueryManager and its subclasses implement make_entry_queries() and make_object_queries() | |
18 | -methods returning lists of Q objects to be applied in a cascade of filter() calls. | |
18 | +QueryManager and its subclasses implement make_queries() method | |
19 | +returning lists of Q objects to be applied in a cascade of filter() calls. | |
19 | 20 | The use of Q objects is necessary to allow for alternatives in queries: |
20 | 21 | the union() and intersection() methods of QuerySets yield a QuerySet that does not support |
21 | 22 | further filtering. |
22 | 23 | ''' |
23 | 24 | |
25 | +DUMMY_LOOKUP = 'DUMMY' | |
26 | + | |
24 | 27 | # https://docs.djangoproject.com/en/2.2/ref/forms/validation/#raising-validationerror |
25 | 28 | # ValidationError params don’t work with str.format(), must use old-style % formatting |
26 | 29 | |
27 | 30 | class QueryManager(object): |
28 | 31 | |
29 | - def __init__(self, entry_lookup, object_lookup, default_conjunction=True): | |
30 | - self.entry_lookup = entry_lookup | |
31 | - self.object_lookup = object_lookup if object_lookup is not None else self.entry_lookup | |
32 | + def __init__(self, lookup, default_conjunction=True): | |
33 | + self.lookup = lookup | |
32 | 34 | self.default_conjunction=default_conjunction |
33 | 35 | |
34 | 36 | # https://stackoverflow.com/questions/310732/in-django-how-does-one-filter-a-queryset-with-dynamic-field-lookups |
35 | 37 | def make_Q(self, lookup, value): |
36 | 38 | return Q(**{lookup : value}) |
37 | 39 | |
38 | - def make_entry_queries(self, value, conjunction): | |
39 | - return self._make_queries(self.entry_lookup, value, conjunction) | |
40 | - | |
41 | - def make_object_queries(self, value): | |
42 | - return self._make_queries(self.object_lookup, value, conjunction=False) | |
40 | + def make_queries(self, value, conjunction): | |
41 | + if self.lookup == DUMMY_LOOKUP: | |
42 | + return [] | |
43 | + return self._make_queries(self.lookup, value, conjunction) | |
43 | 44 | |
44 | 45 | def _make_queries(self, lookup, value, conjunction): |
45 | 46 | raise NotImplementedError |
... | ... | @@ -183,8 +184,8 @@ class ExpressionQueryManager(QueryManager): |
183 | 184 | |
184 | 185 | expr_parser = None |
185 | 186 | |
186 | - def __init__(self, entry_lookup, object_lookup, additional_operators=False, **kwargs): | |
187 | - super().__init__(entry_lookup, object_lookup, **kwargs) | |
187 | + def __init__(self, lookup, additional_operators=False, **kwargs): | |
188 | + super().__init__(lookup, **kwargs) | |
188 | 189 | self.additional_operators = additional_operators |
189 | 190 | |
190 | 191 | def expression_validator(self, value): |
... | ... | @@ -281,6 +282,7 @@ class RangesQueryManager(ExpressionQueryManager): |
281 | 282 | else: |
282 | 283 | return [] |
283 | 284 | |
285 | +''' | |
284 | 286 | # TODO should this inherit after QueryManager??? |
285 | 287 | class OuterQueryMixin(object): |
286 | 288 | |
... | ... | @@ -304,17 +306,13 @@ class OuterQueryMixin(object): |
304 | 306 | objects = objects.filter(query) |
305 | 307 | outer_queries.append(self.make_Q(self.outer_lookup, objects)) |
306 | 308 | return outer_queries |
309 | +''' | |
307 | 310 | |
308 | 311 | # TODO this got complicated, write more comments? |
309 | -class RegexQueryManager(ExpressionQueryManager, OuterQueryMixin): | |
312 | +class RegexQueryManager(ExpressionQueryManager):#, OuterQueryMixin): | |
310 | 313 | |
311 | 314 | expr_parser = RegexAlgebra() |
312 | 315 | |
313 | - def __init__(self, entry_lookup, object_lookup, inner_class=None, outer_lookup=None, **kwargs): | |
314 | - super().__init__(entry_lookup, object_lookup, **kwargs) | |
315 | - self.inner_class = inner_class | |
316 | - self.outer_lookup = outer_lookup | |
317 | - | |
318 | 316 | def literal2query(self, literal, lookup): |
319 | 317 | # a literal may be negated or have no operator attribute |
320 | 318 | try: |
... | ... | @@ -352,8 +350,6 @@ class RegexQueryManager(ExpressionQueryManager, OuterQueryMixin): |
352 | 350 | # give up on generality and implement in subclasses when required? |
353 | 351 | # still looks potentially terribly inefficient... |
354 | 352 | def exclusive_and2queries(self, lookup, value, conjunction): |
355 | - if lookup == self.entry_lookup: | |
356 | - raise RuntimeError | |
357 | 353 | return [] |
358 | 354 | |
359 | 355 | # value has been validated as a proper expression |
... | ... | @@ -372,43 +368,14 @@ class RegexQueryManager(ExpressionQueryManager, OuterQueryMixin): |
372 | 368 | #print(' * '.join(map(str, cnf_exprs)), '\n\n') |
373 | 369 | return [self.cnf2queries(e, lookup) for e in cnf_exprs] |
374 | 370 | |
375 | - def make_object_queries(self, value): | |
371 | + def make_queries(self, value, conjunction): | |
376 | 372 | # _make_queries will return a single list of queries when conjunction=False |
377 | - return self._make_queries(self.object_lookup, value, conjunction=False)[0] | |
378 | - | |
379 | - ''' | |
380 | - # TODO (?): | |
381 | - # Using ‘&’ on Q objects yields the first behavior described in | |
382 | - # https://docs.djangoproject.com/en/2.2/topics/db/queries/#spanning-multi-valued-relationships | |
383 | - # Instead, a cascade of filter() calls seems necessary: | |
384 | - # https://stackoverflow.com/questions/6230897/django-combining-and-and-or-queries-with-manytomany-field | |
385 | - # but to keep consistent with the QueryManager interface (returning lists of Q objects), | |
386 | - # Q objects for individual object specifications are created the ugly way, using the __in lookup | |
387 | - ''' | |
388 | - def make_entry_queries(self, value, conjunction): | |
389 | - if self.outer_lookup is None: | |
390 | - return list(chain(*self._make_queries(self.entry_lookup, value, conjunction))) | |
391 | - else: | |
392 | - return self.make_outer_queries(value) | |
393 | - ''' | |
394 | - object_queries = self._make_queries(self.object_lookup, value, conjunction=True) | |
395 | - entry_queries = [] | |
396 | - print('-------', object_queries) | |
397 | - for queries in object_queries: | |
398 | - if not queries: | |
399 | - continue | |
400 | - print(' ---', queries) | |
401 | - objects = self.inner_class.objects.all() | |
402 | - for query in queries: | |
403 | - objects = objects.filter(query) | |
404 | - entry_queries.append(self.make_Q(self.outer_lookup, objects)) | |
405 | - return entry_queries | |
406 | - ''' | |
373 | + return self._make_queries(self.lookup, value, conjunction=False)[0] | |
407 | 374 | |
408 | 375 | # TODO work-in-progress!!! |
409 | 376 | # for MultiValueField-based filter fields |
410 | 377 | # doesn’t support operator switching for component queries (TODO?) |
411 | -class ComboQueryManager(QueryManager, OuterQueryMixin): | |
378 | +class ComboQueryManager(QueryManager):#, OuterQueryMixin): | |
412 | 379 | |
413 | 380 | def __init__(self, inner_class, outer_lookup, managers, negation_field, **kwargs): |
414 | 381 | super().__init__('foo', 'bar', **kwargs) |
... | ... | @@ -423,7 +390,7 @@ class ComboQueryManager(QueryManager, OuterQueryMixin): |
423 | 390 | raise RuntimeError |
424 | 391 | negate = values[0] if self.negation_field else False |
425 | 392 | query_values = values[1:] if self.negation_field else values |
426 | - queries = [manager.make_object_queries(value) for value, manager in zip(query_values, self.managers) if value is not None] | |
393 | + queries = [manager.make_queries(value) for value, manager in zip(query_values, self.managers) if value is not None] | |
427 | 394 | # The inner_class instances we want to retrieve must satisfy |
428 | 395 | # all the sub-queries at once, so & can be used. |
429 | 396 | # We assume the sub-managers return singleton lists of queries. |
... | ... | @@ -437,9 +404,6 @@ class ComboQueryManager(QueryManager, OuterQueryMixin): |
437 | 404 | else: |
438 | 405 | return [[]] |
439 | 406 | |
440 | - def make_object_queries(self, values): | |
407 | + def make_queries(self, values): | |
441 | 408 | # _make_queries will return a single list of queries |
442 | - return self._make_queries(self.object_lookup, values, conjunction=False)[0] | |
443 | - | |
444 | - def make_entry_queries(self, values, conjunction): | |
445 | - return self.make_outer_queries(values) | |
409 | + return self._make_queries(self.lookup, values, conjunction=False)[0] | |
... | ... |
entries/form_fields/specialised_fields.py
1 | 1 | from django import forms |
2 | 2 | |
3 | +from django.db.models import Q | |
4 | + | |
5 | +from syntax.models_phrase import PhraseTypeModel | |
6 | + | |
3 | 7 | from semantics.models import ( |
4 | 8 | Frame, SemanticRole, RoleAttribute, |
5 | 9 | PredefinedSelectionalPreference, |
... | ... | @@ -17,13 +21,36 @@ from .generic_fields import ( |
17 | 21 | |
18 | 22 | from .query_managers import ( |
19 | 23 | QueryManager, |
24 | + DUMMY_LOOKUP, | |
20 | 25 | ) |
21 | 26 | |
27 | +from entries.polish_strings import PHRASE_TYPE | |
28 | + | |
29 | +# TODO what else, apart from comprepnp, can’t be a lex? | |
30 | +class PhraseTypeFilter(ModelChoiceFilter): | |
31 | + | |
32 | + def __init__(self, lex=False, **kwargs): | |
33 | + super().__init__( | |
34 | + label=('Typ frazeologizmu' if lex else 'Typ frazy'), | |
35 | + queryset=(PhraseTypeModel.objects.exclude(name__in=('lex', 'comprepnp')) if lex else PhraseTypeModel.objects.all()), | |
36 | + key='name', | |
37 | + #lookup='phrase_types__main_type', | |
38 | + # this field is not used for filtering, only for choosing subform type | |
39 | + lookup=DUMMY_LOOKUP, | |
40 | + # we will want to identify the selected phrase type by name later | |
41 | + to_field_name='name', | |
42 | + empty_label='wybierz', | |
43 | + **kwargs, | |
44 | + ) | |
45 | + def label_from_instance(self, obj): | |
46 | + pt = obj.name | |
47 | + return '{} ({})'.format(pt, PHRASE_TYPE.get(pt, '...')) | |
48 | + | |
22 | 49 | class ArgumentFilter(ComboFilter): |
23 | 50 | |
24 | 51 | def __init__(self): |
25 | 52 | super().__init__( |
26 | - label='Argument', | |
53 | + label='Rola semantyczna', | |
27 | 54 | inner_class=Frame, |
28 | 55 | outer_lookup='subentries__schema_hooks__argument_connections__argument__frame__in', |
29 | 56 | fields=[ |
... | ... | @@ -32,16 +59,14 @@ class ArgumentFilter(ComboFilter): |
32 | 59 | empty_label='Rola', |
33 | 60 | queryset=SemanticRole.objects.all(), |
34 | 61 | key='role', |
35 | - entry_lookup=None, | |
36 | - object_lookup='argument__role__role', | |
62 | + lookup='argument__role__role', | |
37 | 63 | ), |
38 | 64 | ModelChoiceFilter( |
39 | 65 | label='Atrybut roli', |
40 | 66 | empty_label='Atrybut roli', |
41 | 67 | queryset=RoleAttribute.objects.all(), |
42 | 68 | key='attribute', |
43 | - entry_lookup=None, | |
44 | - object_lookup='argument__role__attribute', | |
69 | + lookup='argument__role__attribute', | |
45 | 70 | ), |
46 | 71 | ], |
47 | 72 | negation_field=True, |
... | ... | @@ -59,8 +84,7 @@ class PredefinedPreferenceFilter(ComboFilter): |
59 | 84 | empty_label='Predefiniowane', |
60 | 85 | queryset=PredefinedSelectionalPreference.objects.all(), |
61 | 86 | key='key', |
62 | - entry_lookup=None, | |
63 | - object_lookup='argument__predefined', | |
87 | + lookup='argument__predefined', | |
64 | 88 | ), |
65 | 89 | ], |
66 | 90 | negation_field=True, |
... | ... | @@ -78,29 +102,26 @@ class RelationalPreferenceFilter(ComboFilter): |
78 | 102 | empty_label='Relacja', |
79 | 103 | queryset=SelectionalPreferenceRelation.objects.all(), |
80 | 104 | key='key', |
81 | - entry_lookup=None, | |
82 | - object_lookup='argument__relations__relation', | |
105 | + lookup='argument__relations__relation', | |
83 | 106 | ), |
84 | 107 | ModelChoiceFilter( |
85 | 108 | label='Do: rola', |
86 | 109 | empty_label='Do: rola', |
87 | 110 | queryset=SemanticRole.objects.all(), |
88 | 111 | key='role', |
89 | - entry_lookup=None, | |
90 | - object_lookup='argument__relations__to__role__role', | |
112 | + lookup='argument__relations__to__role__role', | |
91 | 113 | ), |
92 | 114 | ModelChoiceFilter( |
93 | 115 | label='Do: atrybut', |
94 | 116 | empty_label='Do: atrybut', |
95 | 117 | queryset=RoleAttribute.objects.all(), |
96 | 118 | key='attribute', |
97 | - entry_lookup=None, | |
98 | - object_lookup='argument__relations__to__role__attribute', | |
119 | + lookup='argument__relations__to__role__attribute', | |
99 | 120 | ), |
100 | 121 | ] |
101 | 122 | ) |
102 | 123 | |
103 | -# TODO add a ‘text_rep’ field to the Synset object | |
124 | +''' | |
104 | 125 | class SynsetPreferenceFilter(ComboFilter): |
105 | 126 | |
106 | 127 | def __init__(self): |
... | ... | @@ -111,13 +132,14 @@ class SynsetPreferenceFilter(ComboFilter): |
111 | 132 | fields=[ |
112 | 133 | SingleRegexFilter( |
113 | 134 | label='Synset', |
114 | - entry_lookup=None, | |
115 | - object_lookup='argument__synsets__lexical_units__text_rep', | |
135 | + lookup='argument__synsets__lexical_units__text_rep', | |
116 | 136 | ), |
117 | 137 | ] |
118 | 138 | ) |
139 | +''' | |
119 | 140 | |
120 | 141 | # TODO implementation without internal QueryManager subclass? |
142 | +# TODO possibly refactor and check | |
121 | 143 | class PhraseoFilter(forms.ChoiceField, RadiosLayoutField): |
122 | 144 | |
123 | 145 | class PhraseoQueryManager(QueryManager): |
... | ... |
entries/forms.py
1 | +from random import randint | |
2 | + | |
1 | 3 | from django import forms |
2 | 4 | |
5 | +from django.db.models import OneToOneField, ForeignKey, CharField, ManyToManyField, Q | |
6 | + | |
3 | 7 | from crispy_forms.helper import FormHelper |
4 | 8 | import crispy_forms.bootstrap as bootstrap |
5 | 9 | import crispy_forms.layout as layout |
6 | 10 | |
7 | 11 | from connections.models import Entry, POS, Status, SchemaHook |
8 | -from syntax.models import Schema, SchemaOpinion, InherentSie, Negativity, Predicativity, Aspect, PhraseType | |
9 | -from semantics.models import FrameOpinion, SemanticRole, RoleAttribute | |
12 | +from syntax.models import ( | |
13 | + Schema, SchemaOpinion, InherentSie, Negativity, Predicativity, Aspect, | |
14 | + Position, SyntacticFunction, Control, PredicativeControl, | |
15 | +) | |
16 | +from syntax.models_phrase import PhraseType, EmptyAttributes | |
17 | + | |
18 | +from semantics.models import ( | |
19 | + Frame, FrameOpinion, | |
20 | + Argument, SemanticRole, RoleAttribute, | |
21 | + PredefinedSelectionalPreference, RelationalSelectionalPreference, | |
22 | +) | |
23 | + | |
24 | +from meanings.models import Synset | |
10 | 25 | |
11 | 26 | from .form_fields.generic_fields import ( |
12 | 27 | RangeFilter, |
... | ... | @@ -20,242 +35,765 @@ from .form_fields.generic_fields import ( |
20 | 35 | |
21 | 36 | from .form_fields.specialised_fields import ( |
22 | 37 | PhraseoFilter, |
38 | + PhraseTypeFilter, | |
23 | 39 | ArgumentFilter, |
24 | 40 | PredefinedPreferenceFilter, |
25 | 41 | RelationalPreferenceFilter, |
26 | - SynsetPreferenceFilter, | |
42 | + #SynsetPreferenceFilter, | |
27 | 43 | ) |
28 | 44 | |
29 | -#from .layouts import Formset | |
45 | +from .form_fields.query_managers import SingleValueQueryManager | |
30 | 46 | |
31 | -from . import polish_strings | |
47 | +# TODO better to import this from the importer, the web app should not be aware of the importer! :) | |
48 | +from importer.PhraseAttributes import attrs_helpers | |
32 | 49 | |
33 | -''' | |
34 | -class ArgumentFiltersForm(forms.Form): | |
35 | - | |
36 | - def __init__(self, *args, **kwargs): | |
37 | - super().__init__(*args, **kwargs) | |
38 | - self.helper = FormHelper() | |
39 | - self.helper.form_method = 'get' | |
40 | - #self.helper.form_action = 'filters:get_entries' | |
41 | - self.helper.form_id = 'argument-filters-form' | |
42 | - #self.helper.form_class = 'form-horizontal' | |
43 | - #self.helper.label_class = 'col-sm-2' | |
44 | - #self.helper.field_class = 'col-sm-8' | |
45 | - | |
46 | - self.helper.layout = layout.Layout( | |
47 | - layout.Row( | |
48 | - layout.Field('argument_role'), | |
49 | - layout.Field('argument_role_attribute'), | |
50 | - ) | |
51 | - ) | |
52 | - argument_role = forms.CharField() | |
53 | - argument_role_attribute = forms.CharField() | |
50 | +from . import polish_strings | |
54 | 51 | |
55 | -ArgumentFormSet = forms.formset_factory(ArgumentFiltersForm) | |
56 | 52 | |
57 | -class ArgumentField(forms.Field, ArgumentFormSet): | |
58 | - | |
59 | - def layout(self, x): | |
60 | - return layout.Fieldset(self.label, Formset(x)) | |
53 | +# TODO get_queries probably needs refactoring now that QueryForm is subclassed | |
54 | +class QueryForm(forms.Form): | |
61 | 55 | |
62 | - def __init__(self, *args, **kwargs): | |
63 | - super().__init__(required=False, *args, **kwargs) | |
64 | - self.label = 'Argumenty semantyczne' | |
65 | -''' | |
66 | - | |
67 | -class FiltersForm(forms.Form): | |
68 | - | |
69 | 56 | def __init__(self, *args, **kwargs): |
70 | 57 | super().__init__(*args, **kwargs) |
71 | 58 | self.helper = FormHelper() |
72 | - self.helper.form_method = 'get' | |
73 | - self.helper.form_action = 'entries:get_entries' | |
74 | - self.helper.form_id = 'filters-form' | |
75 | 59 | self.helper.form_class = 'form-horizontal' |
76 | 60 | self.helper.label_class = 'col-sm-2' |
77 | - self.helper.field_class = 'col-sm-8' | |
78 | - | |
79 | - components = [] | |
80 | - field_groups = [] | |
81 | - all_fields = FiltersForm.base_fields.items() | |
82 | - for gen, name in (('haseł', 'entry_'), ('schematów', 'schema_'), ('ram', 'frame_')): | |
83 | - fields = [i for i in all_fields if name in i[0]] | |
84 | - #field_group = FiltersForm.make_field_group('Filtrowanie według {}'.format(gen), fields, layout.Fieldset) | |
85 | - field_group = FiltersForm.make_field_group('Filtrowanie według {}'.format(gen), fields, bootstrap.Tab) | |
86 | - field_groups.append(field_group) | |
87 | - components.append(bootstrap.TabHolder(*field_groups)) | |
88 | - components.append(layout.Submit('filters-submit', 'Filtruj')) | |
89 | - self.helper.layout = layout.Layout(*components) | |
90 | - | |
91 | - # TODO this is ugly! | |
92 | - def make_field_group(title, fields, cls): | |
93 | - args = [title] | |
94 | - for field_name, field_object in fields: | |
95 | - #print(field_name, repr(field_object), field_object.layout) | |
96 | - args.append(field_object.layout(field_name)) | |
97 | - return cls(*args) | |
98 | - | |
99 | - def get_queries(self, filter_type=None): | |
61 | + self.helper.field_class = 'col-sm-10' | |
62 | + self.helper.attrs = { 'data_formtype' : 'unknown' } | |
63 | + | |
64 | + @staticmethod | |
65 | + def get_child_form_prefix(child_form): | |
66 | + raise NotImplementedError | |
67 | + | |
68 | + def get_queries(self): | |
100 | 69 | queries = [] |
101 | 70 | for key, value in self.cleaned_data.items(): |
102 | - print(key, value) | |
71 | + #print('========', key, value) | |
103 | 72 | if value and not key.startswith('operator') and not key.startswith('filter'): |
104 | - if filter_type is not None and key.split('_')[0] != filter_type: | |
105 | - continue | |
73 | + #if filter_type is not None and key.split('_')[0] != filter_type: | |
74 | + # continue | |
106 | 75 | form_field = self.fields[key] |
107 | 76 | operator_key = 'operator_{}'.format(key) |
108 | 77 | # TODO conjunction variable is only needed if filter_type is None? |
109 | 78 | conjunction = self.cleaned_data.get(operator_key) |
110 | 79 | if conjunction is None: |
111 | 80 | conjunction = form_field.query_manager.default_conjunction |
112 | - if filter_type is not None: | |
113 | - queries += form_field.query_manager.make_object_queries(value) | |
114 | - else: | |
115 | - queries += form_field.query_manager.make_entry_queries(value, conjunction) | |
81 | + queries += form_field.query_manager.make_queries(value, conjunction) | |
116 | 82 | return queries |
117 | 83 | |
118 | - # ENTRY FILTERS ============================================================ | |
119 | - entry_lemma = RegexFilter( | |
84 | + @classmethod | |
85 | + def make_field(cls, field_name): | |
86 | + field_object = cls.base_fields[field_name] | |
87 | + return field_object.layout(field_name) | |
88 | + | |
89 | +def or_button(button_label): | |
90 | + return bootstrap.StrictButton('+ Lub ({})'.format(button_label), css_class='or-button btn-sm btn-info') | |
91 | + | |
92 | +def remove_button(label): | |
93 | + return bootstrap.StrictButton(label, css_class='remove-button btn-sm btn-warning') | |
94 | + | |
95 | +def and_or_form_creator(button_label, field_name=None, data_add=None, *args, **kwargs): | |
96 | + add_button = None | |
97 | + data_add = 'switch' if data_add is None else data_add | |
98 | + if field_name is not None: | |
99 | + add_button = bootstrap.FieldWithButtons(field_name, bootstrap.StrictButton('+ Dodaj', css_class='add-button btn-sm btn-success', data_add=data_add, *args, **kwargs)) | |
100 | + else: | |
101 | + add_button = bootstrap.StrictButton('+ {}'.format(button_label), css_class='add-button btn-sm btn-success', data_add=data_add, *args, **kwargs) | |
102 | + return [ | |
103 | + #layout.HTML('<div class="and-or-forms py-1 pl-1 mb-1 border-top border-bottom border-danger" data-formtype="{}">'.format(data_add)), | |
104 | + layout.HTML('<div class="and-or-forms py-1 pl-1 mb-1" data-formtype="{}">'.format(data_add)), | |
105 | + add_button, | |
106 | + or_button(button_label), | |
107 | + layout.HTML('</div>'), | |
108 | + ] | |
109 | + | |
110 | + | |
111 | +''' | |
112 | +class MainEntryForm(QueryForm): | |
113 | + | |
114 | + def __init__(self, *args, **kwargs): | |
115 | + super().__init__(*args, **kwargs) | |
116 | + self.helper.form_method = 'get' | |
117 | + # TODO remove if this form is to be used with different views | |
118 | + self.helper.form_action = 'entries:get_entries' | |
119 | + self.helper.form_id = 'main-form' | |
120 | + self.helper.attrs = { 'data_formtype' : 'entry-main' } | |
121 | + self.model_class = Entry | |
122 | + | |
123 | + components = [] | |
124 | + components.append(layout.Submit('main-submit', 'Filtruj')) | |
125 | + self.helper.layout = layout.Layout(*components) | |
126 | + | |
127 | + @staticmethod | |
128 | + def get_child_form_prefix(child_form): | |
129 | + if child_form.model_class == Schema: | |
130 | + return 'subentries__schemata__in' | |
131 | + if child_form.model_class == Frame: | |
132 | + return 'subentries__schema_hooks__argument_connections__argument__frame__in' | |
133 | + raise KeyError(type(child_form)) | |
134 | +''' | |
135 | + | |
136 | +class EntryForm(QueryForm): | |
137 | + | |
138 | + def __init__(self, *args, **kwargs): | |
139 | + super().__init__(*args, **kwargs) | |
140 | + #self.helper.form_method = 'get' | |
141 | + # TODO remove if this form is to be used with different views | |
142 | + self.helper.form_action = 'entries:get_entries' | |
143 | + self.helper.form_id = 'main-form' | |
144 | + self.helper.attrs = { 'data_formtype' : 'entry' } | |
145 | + self.model_class = Entry | |
146 | + | |
147 | + entry_components = [ | |
148 | + self.make_field('lemma'), | |
149 | + self.make_field('pos'), | |
150 | + self.make_field('phraseology'), | |
151 | + self.make_field('status'), | |
152 | + ] | |
153 | + | |
154 | + schema_components = [ | |
155 | + self.make_field('num_schemata'), | |
156 | + ] + \ | |
157 | + and_or_form_creator('Schemat', data_add='schema') + \ | |
158 | + [ | |
159 | + self.make_field('filter_schemata'), | |
160 | + ] + \ | |
161 | + and_or_form_creator('Fraza', field_name='phrase_type', data_prefix='phrase_') | |
162 | + | |
163 | + frame_components = [ | |
164 | + self.make_field('num_frames'), | |
165 | + ] + \ | |
166 | + and_or_form_creator('Rama', data_add='frame') + \ | |
167 | + [ | |
168 | + self.make_field('filter_frames'), | |
169 | + ] | |
170 | + | |
171 | + components = [ | |
172 | + layout.Fieldset('Własności haseł', *entry_components), | |
173 | + layout.Fieldset('Własności składniowe', *schema_components), | |
174 | + layout.Fieldset('Własności semantyczne', *frame_components), | |
175 | + layout.Submit('main-submit', 'Filtruj'), | |
176 | + layout.Reset('main-reset', 'Wyczyść'), | |
177 | + ] | |
178 | + | |
179 | + self.helper.layout = layout.Layout(*components) | |
180 | + | |
181 | + lemma = RegexFilter( | |
120 | 182 | label='Lemat', |
121 | - entry_lookup='name', | |
122 | - object_lookup=None, | |
183 | + lookup='name', | |
123 | 184 | max_length=200, |
185 | + autocomplete='lemma', | |
124 | 186 | ) |
125 | - entry_pos = ModelMultipleChoiceFilter( | |
187 | + pos = ModelMultipleChoiceFilter( | |
126 | 188 | label='Część mowy', |
127 | 189 | queryset=POS.objects.exclude(tag='unk'), |
128 | 190 | key='tag', |
129 | 191 | human_values=polish_strings.POS, |
130 | - entry_lookup='pos', | |
131 | - object_lookup=None, | |
192 | + lookup='pos', | |
132 | 193 | ) |
133 | - entry_phraseology = PhraseoFilter() | |
134 | - entry_status = ModelMultipleChoiceFilter( | |
194 | + phraseology = PhraseoFilter() | |
195 | + status = ModelMultipleChoiceFilter( | |
135 | 196 | label ='Status', |
136 | 197 | queryset=Status.objects.all().order_by('-priority'), |
137 | 198 | key = 'key', |
138 | - entry_lookup='status', | |
139 | - object_lookup=None, | |
199 | + lookup='status', | |
140 | 200 | ) |
141 | - entry_num_schemata = RangeFilter( | |
201 | + num_schemata = RangeFilter( | |
142 | 202 | label='Liczba schematów', |
143 | - entry_lookup='schemata_count', | |
144 | - object_lookup=None, | |
203 | + lookup='schemata_count', | |
145 | 204 | ) |
146 | - entry_num_frames = RangeFilter( | |
205 | + num_frames = RangeFilter( | |
147 | 206 | label='Liczba ram', |
148 | - entry_lookup='frames_count', | |
149 | - object_lookup=None, | |
207 | + lookup='frames_count', | |
150 | 208 | ) |
209 | + phrase_type = PhraseTypeFilter(help_text='Typ frazy występujący w haśle.') | |
210 | + filter_schemata = SwitchField('Ukryj niepasujące schematy') | |
211 | + filter_frames = SwitchField('Ukryj niepasujące ramy') | |
151 | 212 | |
152 | - # SCHEMA FILTERS =========================================================== | |
153 | - schema_opinion = ModelMultipleChoiceFilter( | |
154 | - label='Opinia o schemacie', | |
155 | - queryset=SchemaOpinion.objects.all(), | |
156 | - key='key', | |
157 | - human_values=polish_strings.SCHEMA_OPINION, | |
158 | - entry_lookup='subentries__schemata__opinion', | |
159 | - object_lookup='opinion', | |
160 | - #initial=SchemaOpinion.objects.filter(key__in=('cer', 'vul')), | |
161 | - ) | |
162 | - operator_schema_opinion = OperatorField() | |
163 | - schema_type = MultipleChoiceFilter( | |
164 | - label='Typ', | |
165 | - choices=((False, 'normalny'), (True, 'frazeologiczny'),), | |
166 | - entry_lookup='subentries__schemata__phraseologic', | |
167 | - object_lookup='phraseologic', | |
168 | - ) | |
169 | - operator_schema_type = OperatorField() | |
170 | - schema_sie = ModelMultipleChoiceFilter( | |
171 | - label='Zwrotność', | |
172 | - queryset=InherentSie.objects.all().order_by('-priority'), | |
173 | - key='name', | |
174 | - human_values=polish_strings.TRUE_FALSE_YES_NO, | |
175 | - entry_lookup='subentries__schemata__inherent_sie', | |
176 | - object_lookup='inherent_sie', | |
177 | - ) | |
178 | - operator_schema_sie = OperatorField() | |
179 | - schema_neg = ModelMultipleChoiceFilter( | |
180 | - label='Negatywność', | |
181 | - queryset=Negativity.objects.exclude(name='').order_by('-priority'), | |
182 | - key='name', | |
183 | - human_values=polish_strings.NEGATION, | |
184 | - entry_lookup='subentries__negativity', | |
185 | - # both Schema and Entry have the same related name for Subentry: | |
186 | - # can use the same lookup path | |
187 | - object_lookup=None, | |
188 | - ) | |
189 | - operator_schema_neg = OperatorField() | |
190 | - schema_pred = ModelMultipleChoiceFilter( | |
191 | - label='Predykatywność', | |
192 | - queryset=Predicativity.objects.all().order_by('-priority'), | |
193 | - key='name', | |
194 | - human_values=polish_strings.TRUE_FALSE_YES_NO, | |
195 | - entry_lookup='subentries__predicativity', | |
196 | - # both Schema and Entry have the same related name for Subentry: | |
197 | - # can use the same lookup path | |
198 | - object_lookup=None, | |
213 | + @staticmethod | |
214 | + def get_child_form_prefix(child_form): | |
215 | + if child_form.model_class == Schema: | |
216 | + return 'subentries__schemata__in' | |
217 | + if child_form.model_class == PhraseType: | |
218 | + return 'subentries__schemata__positions__phrase_types__in' | |
219 | + if child_form.model_class == Frame: | |
220 | + return 'subentries__schema_hooks__argument_connections__argument__frame__in' | |
221 | + raise KeyError(type(child_form)) | |
222 | + | |
223 | + | |
224 | +# for forms that can appear multiple times in the query consrtuctor field | |
225 | +# (e.g. a phrase form), create a separate form class for each form ‘instance’, | |
226 | +# with globally unique field names to ensure proper javascript behaviour | |
227 | +# and retrieving the field values in views | |
228 | +class FormFactory(object): | |
229 | + | |
230 | + @staticmethod | |
231 | + def unique_number(): | |
232 | + return randint(1000, 12345678) | |
233 | + | |
234 | + @staticmethod | |
235 | + def unique_name(field_name, unique_number): | |
236 | + return '{}_{}'.format(field_name, unique_number) | |
237 | + | |
238 | + @staticmethod | |
239 | + def make_helper_attrs(form_type, unique_number): | |
240 | + return { 'data_formtype' : form_type , 'data_formnumber' : unique_number } | |
241 | + | |
242 | + @staticmethod | |
243 | + def make_layout(title, components): | |
244 | + return layout.Layout(layout.Fieldset( | |
245 | + '{} <button class="btn remove-button btn-sm btn-warning" type="button">Usuń</button>'.format(title), | |
246 | + *components | |
247 | + )) | |
248 | + | |
249 | + @classmethod | |
250 | + def get_form(cls, unique_number=None, *args, **kwargs): | |
251 | + unique_number = cls.unique_number() if unique_number is None else unique_number | |
252 | + form_class = cls.make_form_class(n=unique_number) | |
253 | + for field_name, field_maker in cls.field_makers: | |
254 | + form_class.base_fields[FormFactory.unique_name(field_name, unique_number)] = field_maker() | |
255 | + return form_class(*args, **kwargs) | |
256 | + | |
257 | + # n – unique number for this form used for creating globally unique field names | |
258 | + @classmethod | |
259 | + def make_form_class(cls, n): | |
260 | + raise NotImplementedError | |
261 | + | |
262 | +class SchemaFormFactory(FormFactory): | |
263 | + | |
264 | + field_makers = ( | |
265 | + ( | |
266 | + 'opinion', | |
267 | + lambda: ModelMultipleChoiceFilter( | |
268 | + label='Opinia o schemacie', | |
269 | + queryset=SchemaOpinion.objects.all(), | |
270 | + key='key', | |
271 | + human_values=polish_strings.SCHEMA_OPINION, | |
272 | + lookup='opinion', | |
273 | + #initial=SchemaOpinion.objects.filter(key__in=('col', 'vul')), | |
274 | + ) | |
275 | + ), | |
276 | + #( | |
277 | + # 'operator_opinion', | |
278 | + # lambda: OperatorField() | |
279 | + #), | |
280 | + ( | |
281 | + 'type', | |
282 | + lambda: MultipleChoiceFilter( | |
283 | + label='Typ', | |
284 | + choices=((False, 'zwykły'), (True, 'frazeologiczny'),), | |
285 | + lookup='phraseologic', | |
286 | + ) | |
287 | + ), | |
288 | + #( | |
289 | + # 'operator_type', | |
290 | + # lambda: OperatorField() | |
291 | + #), | |
292 | + ( | |
293 | + 'sie', | |
294 | + lambda: ModelMultipleChoiceFilter( | |
295 | + label='Zwrotność', | |
296 | + queryset=InherentSie.objects.all().order_by('-priority'), | |
297 | + key='name', | |
298 | + human_values=polish_strings.TRUE_FALSE_YES_NO, | |
299 | + lookup='inherent_sie', | |
300 | + ) | |
301 | + ), | |
302 | + #( | |
303 | + # 'operator_sie', | |
304 | + # lambda: OperatorField() | |
305 | + #), | |
306 | + #( | |
307 | + # 'neg', | |
308 | + # lambda: ModelMultipleChoiceFilter( | |
309 | + # label='Negatywność', | |
310 | + # queryset=Negativity.objects.exclude(name='').order_by('-priority'), | |
311 | + # key='name', | |
312 | + # human_values=polish_strings.NEGATION, | |
313 | + # entry_lookup='subentries__negativity', | |
314 | + # # both Schema and Entry have the same related name for Subentry: | |
315 | + # # can use the same lookup path | |
316 | + # object_lookup=None, | |
317 | + # ) | |
318 | + #), | |
319 | + #( | |
320 | + # 'operator_neg', | |
321 | + # lambda: OperatorField() | |
322 | + #), | |
323 | + #( | |
324 | + # 'pred', | |
325 | + # lambda: ModelMultipleChoiceFilter( | |
326 | + # label='Predykatywność', | |
327 | + # queryset=Predicativity.objects.all().order_by('-priority'), | |
328 | + # key='name', | |
329 | + # human_values=polish_strings.TRUE_FALSE_YES_NO, | |
330 | + # entry_lookup='subentries__predicativity', | |
331 | + # # both Schema and Entry have the same related name for Subentry: | |
332 | + # # can use the same lookup path | |
333 | + # object_lookup=None, | |
334 | + # ) | |
335 | + #), | |
336 | + #( | |
337 | + # 'operator_pred', | |
338 | + # lambda: OperatorField() | |
339 | + #), | |
340 | + #( | |
341 | + # 'aspect', | |
342 | + # lambda: ModelMultipleChoiceFilter( | |
343 | + # label='Aspekt', | |
344 | + # queryset=Aspect.objects.exclude(name='').exclude(name='_').order_by('-priority'), | |
345 | + # key='name', | |
346 | + # human_values=polish_strings.ASPECT, | |
347 | + # entry_lookup='subentries__aspect', | |
348 | + # # both Schema and Entry have the same related name for Subentry: | |
349 | + # # can use the same lookup path | |
350 | + # object_lookup=None, | |
351 | + # ) | |
352 | + #), | |
353 | + #( | |
354 | + # 'operator_aspect', | |
355 | + # lambda: OperatorField() | |
356 | + #), | |
357 | + #phrase = RegexFilter( | |
358 | + #label='Fraza', | |
359 | + #max_length=200, | |
360 | + ##initial='np.* !& !np.* | xp.*', | |
361 | + #lookup='positions__phrase_types__text_rep', | |
362 | + #additional_operators=True, | |
363 | + #autocomplete='phrasetype', | |
364 | + #) | |
365 | + #( | |
366 | + # 'num_positions', | |
367 | + # lambda: RangeFilter( | |
368 | + # label='Liczba pozycyj', | |
369 | + # entry_lookup='subentries__schemata__positions_count', | |
370 | + # object_lookup='positions_count', | |
371 | + # ) | |
372 | + #), | |
373 | + #( | |
374 | + # 'num_phrase_types', | |
375 | + # lambda: RangeFilter( | |
376 | + # label='Liczba typów fraz w pozycji', | |
377 | + # entry_lookup='subentries__schemata__positions__phrases_count', | |
378 | + # object_lookup='positions__phrases_count', | |
379 | + # ) | |
380 | + #), | |
199 | 381 | ) |
200 | - operator_schema_pred = OperatorField() | |
201 | - schema_aspect = ModelMultipleChoiceFilter( | |
202 | - label='Aspekt', | |
203 | - queryset=Aspect.objects.exclude(name='').exclude(name='_').order_by('-priority'), | |
204 | - key='name', | |
205 | - human_values=polish_strings.ASPECT, | |
206 | - entry_lookup='subentries__aspect', | |
207 | - # both Schema and Entry have the same related name for Subentry: | |
208 | - # can use the same lookup path | |
209 | - object_lookup=None, | |
382 | + | |
383 | + @classmethod | |
384 | + def make_form_class(cls, n): | |
385 | + | |
386 | + class SchemaForm(QueryForm): | |
387 | + | |
388 | + def __init__(self, *args, **kwargs): | |
389 | + super().__init__(*args, **kwargs) | |
390 | + self.helper.attrs = cls.make_helper_attrs('schema', n) | |
391 | + self.model_class = Schema | |
392 | + | |
393 | + components = [ | |
394 | + #remove_button('Usuń schemat'), | |
395 | + self.make_field(cls.unique_name('opinion', n)), | |
396 | + #self.make_field(cls.unique_name('operator_opinion', n)), | |
397 | + self.make_field(cls.unique_name('type', n)), | |
398 | + self.make_field(cls.unique_name('sie', n)), | |
399 | + #self.make_field(cls.unique_name('phrase', n)), | |
400 | + ] + \ | |
401 | + and_or_form_creator('Pozycja', data_add='position') | |
402 | + self.helper.layout = cls.make_layout('Schemat składniowy', components) | |
403 | + | |
404 | + @staticmethod | |
405 | + def get_child_form_prefix(child_form): | |
406 | + if child_form.model_class == Position: | |
407 | + return 'positions__in' | |
408 | + raise KeyError(type(child_form)) | |
409 | + | |
410 | + return SchemaForm | |
411 | + | |
412 | + | |
413 | +class PositionFormFactory(FormFactory): | |
414 | + | |
415 | + field_makers = ( | |
416 | + ( | |
417 | + 'gram_function', | |
418 | + lambda: ModelMultipleChoiceFilter( | |
419 | + label='Funkcja gramatyczna', | |
420 | + queryset=SyntacticFunction.objects.all(), | |
421 | + key='name', | |
422 | + lookup='function', | |
423 | + ) | |
424 | + ), | |
425 | + ( | |
426 | + 'control', | |
427 | + lambda: ModelMultipleChoiceFilter( | |
428 | + label='Kontrola', | |
429 | + queryset=Control.objects.all(), | |
430 | + key='name', | |
431 | + lookup='control', | |
432 | + ) | |
433 | + ), | |
434 | + ( | |
435 | + 'pred_control', | |
436 | + lambda: ModelMultipleChoiceFilter( | |
437 | + label='Kontrola predykatywna', | |
438 | + queryset=PredicativeControl.objects.all(), | |
439 | + key='name', | |
440 | + lookup='pred_control', | |
441 | + ) | |
442 | + ), | |
443 | + ( | |
444 | + 'phrase_type', | |
445 | + lambda: PhraseTypeFilter(help_text='Typ frazy występujący na pozycji.') | |
446 | + ), | |
210 | 447 | ) |
211 | - operator_schema_aspect = OperatorField() | |
212 | - schema_phrase = RegexFilter( | |
213 | - label='Fraza', | |
214 | - max_length=200, | |
215 | - #initial='.*ręka.* | [xp\(abl\) & xp\(adl\) & [xp\(dur\) | xp\(caus\)]]', | |
216 | - #initial='xp\(abl\) & xp\(adl\)', | |
217 | - #initial='np.* !& !np.* | xp.*', | |
218 | - #entry_lookup='subentries__schemata__position__phrase_types__text_rep', | |
219 | - entry_lookup=None, | |
220 | - object_lookup='positions__phrase_types__text_rep', | |
221 | - inner_class=Schema, | |
222 | - outer_lookup='subentries__schemata__in', | |
223 | - additional_operators=True, | |
448 | + | |
449 | + @classmethod | |
450 | + def make_form_class(cls, n): | |
451 | + | |
452 | + class PositionForm(QueryForm): | |
453 | + | |
454 | + def __init__(self, *args, **kwargs): | |
455 | + super().__init__(*args, **kwargs) | |
456 | + self.helper.attrs = cls.make_helper_attrs('position', n) | |
457 | + self.model_class = Position | |
458 | + | |
459 | + components = [ | |
460 | + self.make_field(cls.unique_name('gram_function', n)), | |
461 | + self.make_field(cls.unique_name('control', n)), | |
462 | + self.make_field(cls.unique_name('pred_control', n)), | |
463 | + ] + \ | |
464 | + and_or_form_creator('Fraza', field_name=(cls.unique_name('phrase_type', n)), data_prefix='phrase_') | |
465 | + self.helper.layout = cls.make_layout('Pozycja schematu', components) | |
466 | + | |
467 | + @staticmethod | |
468 | + def get_child_form_prefix(child_form): | |
469 | + if child_form.model_class == PhraseType: | |
470 | + return 'phrase_types__in' | |
471 | + raise KeyError(type(child_form)) | |
472 | + | |
473 | + return PositionForm | |
474 | + | |
475 | + | |
476 | +class LexFormFactory(FormFactory): | |
477 | + | |
478 | + field_makers = ( | |
479 | + ( | |
480 | + 'lex_type', | |
481 | + lambda: PhraseTypeFilter(lex=True, help_text='Typ składniowy frazeologizmu.') | |
482 | + ), | |
224 | 483 | ) |
225 | - schema_num_positions = RangeFilter( | |
226 | - label='Liczba pozycyj', | |
227 | - entry_lookup='subentries__schemata__positions_count', | |
228 | - object_lookup='positions_count', | |
484 | + | |
485 | + @classmethod | |
486 | + def make_form_class(cls, n): | |
487 | + | |
488 | + class LexForm(QueryForm): | |
489 | + | |
490 | + def __init__(self, *args, **kwargs): | |
491 | + super().__init__(*args, **kwargs) | |
492 | + self.helper.attrs = cls.make_helper_attrs('lex', n) | |
493 | + self.model_class = PhraseType | |
494 | + | |
495 | + components = and_or_form_creator('Typ frazeologizmu', field_name=cls.unique_name('lex_type', n), data_prefix='phrase_lex') | |
496 | + self.helper.layout = cls.make_layout('Frazeologizm', components) | |
497 | + | |
498 | + return LexForm | |
499 | + | |
500 | +class FrameFormFactory(FormFactory): | |
501 | + | |
502 | + field_makers = ( | |
503 | + ( | |
504 | + 'opinion', | |
505 | + lambda: ModelMultipleChoiceFilter( | |
506 | + label='Opinia', | |
507 | + queryset=FrameOpinion.objects.exclude(key='unk'), | |
508 | + key='key', | |
509 | + human_values=polish_strings.FRAME_OPINION, | |
510 | + lookup='opinion', | |
511 | + ) | |
512 | + ), | |
513 | + #( | |
514 | + # 'operator_opinion', | |
515 | + # lambda: OperatorField() | |
516 | + #), | |
517 | + ( | |
518 | + 'num_arguments', | |
519 | + lambda: RangeFilter( | |
520 | + label='Liczba argumentów', | |
521 | + lookup='arguments_count', | |
522 | + ) | |
523 | + ), | |
524 | + ( | |
525 | + 'num_preferences', | |
526 | + lambda: RangeFilter( | |
527 | + label='Liczba preferencyj selekcyjnych argumentu', | |
528 | + lookup='argument__preferences_count', | |
529 | + ) | |
530 | + ), | |
531 | + # TODO move this to the entry form | |
532 | + #filter_frame_ = SwitchField('Filtruj ramy') | |
229 | 533 | ) |
230 | - schema_num_phrase_types = RangeFilter( | |
231 | - label='Liczba typów fraz w pozycji', | |
232 | - entry_lookup='subentries__schemata__positions__phrases_count', | |
233 | - object_lookup='positions__phrases_count', | |
534 | + | |
535 | + @classmethod | |
536 | + def make_form_class(cls, n): | |
537 | + | |
538 | + class FrameForm(QueryForm): | |
539 | + | |
540 | + def __init__(self, *args, **kwargs): | |
541 | + super().__init__(*args, **kwargs) | |
542 | + self.helper.attrs = cls.make_helper_attrs('frame', n) | |
543 | + self.model_class = Frame | |
544 | + | |
545 | + components = [ | |
546 | + self.make_field(cls.unique_name('opinion', n)), | |
547 | + #self.make_field(cls.unique_name('operator_opinion', n)), | |
548 | + self.make_field(cls.unique_name('num_arguments', n)), | |
549 | + self.make_field(cls.unique_name('num_preferences', n)), | |
550 | + ] + \ | |
551 | + and_or_form_creator('Argument', data_add='argument') | |
552 | + self.helper.layout = cls.make_layout('Rama semantyczna', components) | |
553 | + | |
554 | + return FrameForm | |
555 | + | |
556 | + | |
557 | +class ArgumentFormFactory(FormFactory): | |
558 | + | |
559 | + field_makers = ( | |
560 | + ( | |
561 | + 'role', | |
562 | + lambda: ArgumentFilter(), | |
563 | + ), | |
564 | + ( | |
565 | + 'preference_type', | |
566 | + lambda: forms.ChoiceField( | |
567 | + label='Preferencja selekcyjna', | |
568 | + choices=(('predefined', 'Predefinowana grupa znaczeń'), ('relational', 'Wyrażona przez relację'), ('synset', 'Wyrażona przez jednostkę leksykalną Słowosieci')), | |
569 | + required=False, | |
570 | + ) | |
571 | + ), | |
572 | + ( | |
573 | + 'phrase_type', | |
574 | + lambda: PhraseTypeFilter(help_text='Typ frazy, przez którą może być realizowany argument.') | |
575 | + ), | |
234 | 576 | ) |
235 | - filter_schema_ = SwitchField('Filtruj schematy') | |
236 | - | |
237 | - # FRAME FILTERS ============================================================ | |
238 | - frame_opinion = ModelMultipleChoiceFilter( | |
239 | - label='Opinia', | |
240 | - queryset=FrameOpinion.objects.exclude(key='unk').order_by('-priority'), | |
241 | - key='key', | |
242 | - human_values=polish_strings.FRAME_OPINION, | |
243 | - entry_lookup='subentries__schema_hooks__argument_connections__argument__frame__opinion', | |
244 | - object_lookup='opinion', | |
577 | + | |
578 | + @classmethod | |
579 | + def make_form_class(cls, n): | |
580 | + | |
581 | + class ArgumentForm(QueryForm): | |
582 | + | |
583 | + def __init__(self, *args, **kwargs): | |
584 | + super().__init__(*args, **kwargs) | |
585 | + self.helper.attrs = cls.make_helper_attrs('argument', n) | |
586 | + self.model_class = Argument | |
587 | + | |
588 | + components = [ | |
589 | + self.make_field(cls.unique_name('role', n)), | |
590 | + ] + \ | |
591 | + and_or_form_creator('Preferencja selekcyjna', field_name=cls.unique_name('preference_type', n)) + \ | |
592 | + and_or_form_creator('Pozycja', data_add='position') + \ | |
593 | + and_or_form_creator('Fraza', field_name=cls.unique_name('phrase_type', n), data_prefix='phrase_') | |
594 | + self.helper.layout = cls.make_layout('Argument ramy', components) | |
595 | + | |
596 | + return ArgumentForm | |
597 | + | |
598 | + | |
599 | +class PredefinedPreferenceFormFactory(FormFactory): | |
600 | + | |
601 | + field_makers = ( | |
602 | + ( | |
603 | + 'predefined', | |
604 | + lambda: PredefinedPreferenceFilter() | |
605 | + ), | |
245 | 606 | ) |
246 | - operator_frame_opinion = OperatorField() | |
247 | - frame_argument = ArgumentFilter() | |
248 | - frame_predefined = PredefinedPreferenceFilter() | |
249 | - frame_relational = RelationalPreferenceFilter() | |
250 | - frame_synset = SynsetPreferenceFilter() | |
251 | - frame_num_arguments = RangeFilter( | |
252 | - label='Liczba argumentów', | |
253 | - entry_lookup='subentries__schema_hooks__argument_connections__argument__frame__arguments_count', | |
254 | - object_lookup='arguments_count', | |
607 | + | |
608 | + @classmethod | |
609 | + def make_form_class(cls, n): | |
610 | + | |
611 | + class PredefinedPreferenceForm(QueryForm): | |
612 | + | |
613 | + def __init__(self, *args, **kwargs): | |
614 | + super().__init__(*args, **kwargs) | |
615 | + self.helper.attrs = cls.make_helper_attrs('predefined', n) | |
616 | + self.model_class = PredefinedSelectionalPreference | |
617 | + | |
618 | + components = [ | |
619 | + self.make_field(cls.unique_name('predefined', n)), | |
620 | + ] | |
621 | + self.helper.layout = cls.make_layout('Preferencja predefiniowana', components) | |
622 | + | |
623 | + return PredefinedPreferenceForm | |
624 | + | |
625 | + | |
626 | +class RelationalPreferenceFormFactory(FormFactory): | |
627 | + | |
628 | + field_makers = ( | |
629 | + ( | |
630 | + 'relational', | |
631 | + lambda: RelationalPreferenceFilter() | |
632 | + ), | |
255 | 633 | ) |
256 | - frame_num_preferences = RangeFilter( | |
257 | - label='Liczba preferencyj selekcyjnych argumentu', | |
258 | - entry_lookup='subentries__schema_hooks__argument_connections__argument__preferences_count', | |
259 | - object_lookup='argument__preferences_count', | |
634 | + | |
635 | + @classmethod | |
636 | + def make_form_class(cls, n): | |
637 | + | |
638 | + class RelationalPreferenceForm(QueryForm): | |
639 | + | |
640 | + def __init__(self, *args, **kwargs): | |
641 | + super().__init__(*args, **kwargs) | |
642 | + self.helper.attrs = cls.make_helper_attrs('relational', n) | |
643 | + self.model_class = RelationalSelectionalPreference | |
644 | + | |
645 | + components = [ | |
646 | + self.make_field(cls.unique_name('relational', n)), | |
647 | + ] | |
648 | + self.helper.layout = cls.make_layout('Preferencja – relacja', components) | |
649 | + | |
650 | + return RelationalPreferenceForm | |
651 | + | |
652 | + | |
653 | +class SynsetPreferenceFormFactory(FormFactory): | |
654 | + | |
655 | + field_makers = ( | |
656 | + ( | |
657 | + 'synset', | |
658 | + #lambda: SynsetPreferenceFilter() | |
659 | + lambda: RegexFilter( | |
660 | + label='Jednostka leksykalna', | |
661 | + max_length=200, | |
662 | + lookup='lexical_units__text_rep', | |
663 | + autocomplete='lu' | |
664 | + ) | |
665 | + ), | |
260 | 666 | ) |
261 | - filter_frame_ = SwitchField('Filtruj ramy') | |
667 | + | |
668 | + @classmethod | |
669 | + def make_form_class(cls, n): | |
670 | + | |
671 | + class SynsetPreferenceForm(QueryForm): | |
672 | + | |
673 | + def __init__(self, *args, **kwargs): | |
674 | + super().__init__(*args, **kwargs) | |
675 | + self.helper.attrs = cls.make_helper_attrs('synset', n) | |
676 | + # TODO | |
677 | + self.model_class = Synset | |
678 | + | |
679 | + components = [ | |
680 | + self.make_field(cls.unique_name('synset', n)), | |
681 | + ] | |
682 | + self.helper.layout = cls.make_layout('Preferencja – Słowosieć', components) | |
683 | + | |
684 | + return SynsetPreferenceForm | |
685 | + | |
686 | + | |
687 | +class PhraseAttributesFormFactory(FormFactory): | |
688 | + | |
689 | + # field types are dynamic here | |
690 | + field_makers = None | |
691 | + | |
692 | + qm = SingleValueQueryManager(lookup='main_type__name') | |
693 | + | |
694 | + @classmethod | |
695 | + def make_form_class(cls, n, model, lex_model, phrase_type): | |
696 | + fields = list(filter(lambda x: type(x) in (OneToOneField, ForeignKey, CharField, ManyToManyField) and not x.name.endswith('_ptr'), model._meta.get_fields())) | |
697 | + if lex_model is not None: | |
698 | + lex_fields = list(filter(lambda x: type(x) in (OneToOneField, ForeignKey, CharField, ManyToManyField) and not x.name.endswith('_ptr'), lex_model._meta.get_fields())) | |
699 | + else: | |
700 | + lex_fields = [] | |
701 | + all_fields = [(f, 'phrase_{}'.format(f.name)) for f in fields] + [(f, 'lex_phrase_{}'.format(f.name)) for f in lex_fields] | |
702 | + | |
703 | + class AttributesForm(QueryForm): | |
704 | + | |
705 | + def __init__(self, *args, **kwargs): | |
706 | + super().__init__(*args, **kwargs) | |
707 | + self.helper.attrs = cls.make_helper_attrs('phrase_{}'.format(phrase_type), n) | |
708 | + self.helper.form_id = 'phrase-form' | |
709 | + self.model_class = PhraseType | |
710 | + | |
711 | + components = [] | |
712 | + for _, name in all_fields: | |
713 | + uniq_name = cls.unique_name(name, n) | |
714 | + if name.endswith('_lexes'): | |
715 | + flds = and_or_form_creator('Fraza', field_name=uniq_name, data_prefix='phrase_lex') | |
716 | + elif name.endswith('_lex'): | |
717 | + flds = and_or_form_creator('Fraza', field_name=uniq_name, data_prefix='phrase_lex') | |
718 | + elif name.endswith('_phrase'): | |
719 | + flds = and_or_form_creator('Fraza', field_name=uniq_name, data_prefix='phrase_') | |
720 | + else: | |
721 | + flds = [self.make_field(uniq_name)] | |
722 | + components += flds | |
723 | + pt = polish_strings.PHRASE_TYPE[phrase_type] | |
724 | + header = pt.capitalize() if phrase_type in polish_strings.NO_PHRASE_NAME else 'Fraza {}'.format(pt) | |
725 | + if lex_model: | |
726 | + header += ' (frazeologizm)' | |
727 | + self.helper.layout = cls.make_layout( | |
728 | + header, | |
729 | + #'{} {}'.format('Frazeologizm' if lex_model else 'Fraza', polish_strings.PHRASE_TYPE[phrase_type]), | |
730 | + components | |
731 | + ) | |
732 | + | |
733 | + # add a phrase type query | |
734 | + def get_queries(self): | |
735 | + return super().get_queries() + cls.qm.make_queries(phrase_type, None) | |
736 | + | |
737 | + | |
738 | + for field, name in all_fields: | |
739 | + #print('***', name, field.name, type(field)) | |
740 | + if field.name == 'lexes': | |
741 | + form_field = PhraseTypeFilter(help_text='Fraza składowa frazeologizmu porównawczego.', lex=True) | |
742 | + elif field.name == 'lex': | |
743 | + form_field = PhraseTypeFilter(help_text='Fraza realizująca frazeologizm.') | |
744 | + elif field.name == 'phrase': | |
745 | + form_field = PhraseTypeFilter(help_text='Typ składniowy frazeologizmu.') | |
746 | + elif type(field) == CharField: | |
747 | + # TODO other autocompletes? | |
748 | + autocomplete = 'fixed' if field.name == 'text' else None | |
749 | + form_field = RegexFilter( | |
750 | + label=polish_strings.PHRASE_ATTRIBUTE.get(field.name, field.name), | |
751 | + lookup='attributes__{}attributes__{}'.format(phrase_type, field.name), | |
752 | + max_length=200, | |
753 | + autocomplete=autocomplete, | |
754 | + ) | |
755 | + else: | |
756 | + queryset_q = Q(('{}{}attributes__isnull'.format('lex' if name.startswith('lex') else '', phrase_type), False)) | |
757 | + form_field = ModelMultipleChoiceFilter( | |
758 | + label=polish_strings.PHRASE_ATTRIBUTE.get(field.name, field.name), | |
759 | + queryset=type(field.related_model()).objects.filter(queryset_q).distinct(), | |
760 | + key='name', | |
761 | + lookup='attributes__{}attributes__{}'.format(phrase_type, field.name), | |
762 | + ) | |
763 | + AttributesForm.base_fields[cls.unique_name(name, n)] = form_field | |
764 | + | |
765 | + return AttributesForm | |
766 | + | |
767 | + ''' | |
768 | + @staticmethod | |
769 | + def PhraseAttributesForm(phrase_type, *args, **kwargs): | |
770 | + lex = phrase_type.startswith('lex') | |
771 | + phrase_type = phrase_type[3:] if lex else phrase_type | |
772 | + if phrase_type in attrs_helpers: | |
773 | + helper = attrs_helpers[phrase_type] | |
774 | + cls, lex_cls = helper.attrs_cls, helper.lex_attrs_cls | |
775 | + if lex: | |
776 | + assert(lex_cls is not None) | |
777 | + return PhraseAttributesFormFactory.get_attributes_form(cls, lex_cls, phrase_type, *args, **kwargs) | |
778 | + else: | |
779 | + return PhraseAttributesFormFactory.get_attributes_form(cls, None, phrase_type, *args, **kwargs) | |
780 | + return PhraseAttributesFormFactory.get_attributes_form(EmptyAttributes, None, phrase_type) | |
781 | + ''' | |
782 | + | |
783 | + @classmethod | |
784 | + def get_form(cls, phrase_type, unique_number=None, *args, **kwargs): | |
785 | + unique_number = cls.unique_number() if unique_number is None else unique_number | |
786 | + lex = phrase_type.startswith('lex') | |
787 | + phrase_type = phrase_type[3:] if lex else phrase_type | |
788 | + form_class = None | |
789 | + if phrase_type in attrs_helpers: | |
790 | + helper = attrs_helpers[phrase_type] | |
791 | + attrs_cls, lex_attrs_cls = helper.attrs_cls, helper.lex_attrs_cls | |
792 | + if lex: | |
793 | + assert(lex_attrs_cls is not None) | |
794 | + form_class = cls.make_form_class(unique_number, attrs_cls, lex_attrs_cls, phrase_type) | |
795 | + else: | |
796 | + form_class = cls.make_form_class(unique_number, attrs_cls, None, phrase_type) | |
797 | + if form_class is None: | |
798 | + form_class = cls.make_form_class(unique_number, EmptyAttributes, None, phrase_type) | |
799 | + return form_class(*args, **kwargs) | |
... | ... |
entries/phrase_descriptions/descriptions.py
... | ... | @@ -32,7 +32,7 @@ def phrase_description2(phrase, position, negativity): |
32 | 32 | return desc |
33 | 33 | |
34 | 34 | def phrase_description(phrase, function, negativity, desc_case='nom', inside_lex=False): |
35 | - #print('******', function, '***', negativity, '***', str(phrase)) | |
35 | + print('******', function, '***', negativity, '***', str(phrase)) | |
36 | 36 | if str(phrase) in ( |
37 | 37 | # malowany -> ppas in in Morfeusz |
38 | 38 | 'lex(adjp(agr),agr,agr,pos,malować,natr)', |
... | ... | @@ -46,6 +46,14 @@ def phrase_description(phrase, function, negativity, desc_case='nom', inside_lex |
46 | 46 | 'lex(cp(int[jak]),aff,dziękować,,atr)', |
47 | 47 | # „na jakim świecie żyje” – element pytajny zagnieżdżony w prepnp |
48 | 48 | 'lex(cp(int[jaki]),aff,żyć,,ratr1({lex(prepnp(na,loc),sg,świat,ratr1({lex(adjp(agr),agr,agr,pos,jaki,natr)}))}))', |
49 | + # „co” nie ma wśród modyfikacyj | |
50 | + 'lex(xp(mod[cp(rel[co])]),aff,wyskoczyć,,ratr1(subj{lex(np(str),sg,koń,natr)}))', | |
51 | + 'lex(cp(rel[co]),aff,XOR(przynieść,przynosić),,ratr(subj{lex(np(str),sg,ślina,natr)}+{lex(np(dat),_,XOR(ja,my,on,ty,wy),natr)}+{lex(prepnp(na,acc),_,język,natr)}))', | |
52 | + 'lex(cp(rel[co]),_,XOR(przychodzić,przyjść),,ratr({lex(np(dat),_,XOR(ja,my,on,ty,wy),natr)}+{lex(prepnp(na,acc),sg,myśl,natr)}))', | |
53 | + 'lex(np(str),sg,wszystko,ratr(subj{lex(np(str),sg,co,natr)}+{lex(cp(rel[co]),aff,być,,ratr1({lex(prepnp(w,loc),sg,moc,atr({lex(adjp(agr),agr,agr,pos,ludzki,natr)}))}))}))', | |
54 | + 'lex(np(str),sg,wszystko,ratr(subj{lex(np(str),sg,co,natr)}+{lex(cp(rel[co]),aff,być,,ratr1({lex(prepnp(w,loc),sg,moc,atr({possp}))}))}))', | |
55 | + # „jakby” nie ma wśród modyfikacyj | |
56 | + 'lex(xp(mod[cp(rel[jakby])]),aff,strzelić,,ratr({prepnp(w,acc)}+{lex(np(str),sg,XOR(grom,piorun),natr)}))', | |
49 | 57 | ): |
50 | 58 | return '??? TODO' |
51 | 59 | if str(phrase).startswith('lex'): |
... | ... | @@ -91,10 +99,14 @@ def get_phrase_type(lex_phrase): |
91 | 99 | print(ptype) |
92 | 100 | 1/0 |
93 | 101 | |
102 | +def postprocess_phraseologism(p): | |
103 | + return p.replace(' ,', ',') | |
104 | + | |
94 | 105 | def lex_phrase_description(phrase, function, negativity, desc_case='nom'): |
95 | 106 | phrase2 = get_phrase_type(phrase) |
96 | 107 | desc = make_phrase_description(phrase2, function, negativity, 'inst', inside_lex=True) |
97 | - return 'frazeologizm będący ' + desc + ' postaci' + make_ul(map('<i>{}</i>'.format, uniq_list(make_phraseologisms(phrase, function, negativity)))) | |
108 | + phraseo = uniq_list(map(postprocess_phraseologism, make_phraseologisms(phrase, function, negativity))) | |
109 | + return 'frazeologizm będący ' + desc + ' postaci' + make_ul(map('<i>{}</i>'.format, phraseo)) | |
98 | 110 | |
99 | 111 | def make_phrase_description(phrase, function, negativity, desc_case, inside_lex=False): |
100 | 112 | ptype = type(phrase) |
... | ... | @@ -220,6 +232,7 @@ def combine(phrase, texts): |
220 | 232 | return list(chain.from_iterable(map(joiner.join, powerset_nonempty(x)) for x in product(*texts))) |
221 | 233 | |
222 | 234 | def make_phraseologisms(phrase, function, negativity, attrs={}): |
235 | + # TODO wok versions of preposisions, e.g. ze skóry | |
223 | 236 | ptype = type(phrase) |
224 | 237 | if ptype in (NP, PrepNP, ComPrepNP): |
225 | 238 | # “any ((com)prep)np” |
... | ... | @@ -263,7 +276,7 @@ def make_phraseologisms(phrase, function, negativity, attrs={}): |
263 | 276 | nps += make_modified_phrases(phrase, orth, NP, function, negativity, mod_attrs) |
264 | 277 | return ['{} {}'.format(prep, np) for np in nps] |
265 | 278 | if ptype in (LexNumP, LexPrepNumP): |
266 | - case = phrase._nump._case._value if ptype == LexNumP else phrase._prepnump._prep._case._value | |
279 | + case = (phrase._nump if ptype == LexNumP else phrase._prepnump._prep)._case._value | |
267 | 280 | prep = (phrase._prepnump._prep._value + ' ') if ptype != LexNumP else '' |
268 | 281 | CASE = correct_case(case, function) |
269 | 282 | phrs = [] |
... | ... | @@ -280,7 +293,7 @@ def make_phraseologisms(phrase, function, negativity, attrs={}): |
280 | 293 | for word in words: |
281 | 294 | # wiele wody |
282 | 295 | NUM = correct_num(num, 'pl') if word != 'woda' else 'sg' |
283 | - gend = get_gender_for_num(word) | |
296 | + gend = get_gender(word) | |
284 | 297 | # gender before congr/rec to avoid empty result due to filtering priority |
285 | 298 | feats = [POS, NUM, CASE] + gend + [correct_congr(num)] |
286 | 299 | num_form = get_form(correct_num_lemma(num), feats) |
... | ... | @@ -387,16 +400,17 @@ def make_phraseologisms(phrase, function, negativity, attrs={}): |
387 | 400 | elif typ == 'rel': |
388 | 401 | #ktory = get_form('który', ['adj', attrs['num'], 'nom', attrs['gend'], 'pos'])[0] |
389 | 402 | #conj = '{}/co'.format(ktory) |
390 | - conj = 'co/gdzie/kto…'.format(ktory) | |
403 | + conj = 'co/gdzie/kto…' | |
391 | 404 | elif typ in ('gdy', 'jak', 'kiedy', 'że', 'żeby',): |
392 | 405 | conj = typ |
393 | 406 | if conj is not None: |
394 | 407 | return ['{}{}, {} …'.format(prep, to, conj)] |
395 | 408 | print('===========', typ) |
396 | 409 | 1/0 |
397 | - # TODO order, mnie -> mi itp.,... | |
410 | + # TODO order (się) | |
398 | 411 | if ptype in (LexCP, LexNCP): |
399 | - typ = phrase._cp._type._value if ptype == LexCP else phrase._ncp._type._value | |
412 | + print(phrase) | |
413 | + typ = (phrase._cp if ptype == LexCP else phrase._ncp)._type._value | |
400 | 414 | to = '' if ptype == LexCP else '{}, '.format(TO[phrase._ncp._case._value]) |
401 | 415 | comp = '' |
402 | 416 | if typ == 'żeby2': |
... | ... | @@ -408,12 +422,16 @@ def make_phraseologisms(phrase, function, negativity, attrs={}): |
408 | 422 | neg = correct_neg(phrase._negativity) |
409 | 423 | sie = correct_sie(phrase._inherent_sie) |
410 | 424 | subj = None |
411 | - # dependent like „co”, „na kogo” – should go first | |
425 | + # dependent like „co”, „na kogo”, „który” – should go first | |
412 | 426 | first = [] |
427 | + # then pronouns: mi, ci etc., generic NP: ktoś/coś, LexQub: tylko etc. | |
428 | + pron = [] | |
413 | 429 | rest = [] |
414 | - print() | |
430 | + #print() | |
431 | + realisations = (phrase._cp if ptype == LexCP else phrase._ncp)._type._realisations | |
432 | + realisations = set(realisations) if realisations else set() | |
415 | 433 | for position in phrase._modification._dependents: |
416 | - print('---') | |
434 | + #print('---') | |
417 | 435 | assert(len(position._phrases) == 1) |
418 | 436 | dep_phrase = position._phrases[0] |
419 | 437 | func = position._function._value if position._function else None |
... | ... | @@ -427,36 +445,50 @@ def make_phraseologisms(phrase, function, negativity, attrs={}): |
427 | 445 | words = dep_phrase._lex._words._lemmas |
428 | 446 | elif hasattr(dep_phrase, '_words'): |
429 | 447 | words = dep_phrase._words._lemmas |
430 | - print(words) | |
431 | - if words and {'co', 'gdzie', 'ile', 'jak', 'skąd'}.intersection(words): | |
448 | + #print(words) | |
449 | + if words: | |
450 | + realisations.difference_update(words) | |
451 | + if words and {'co', 'gdzie', 'ile', 'jak', 'skąd', 'dokąd', 'który',}.intersection(words): | |
432 | 452 | first.append(dep_phr) |
433 | 453 | elif func != 'subj': |
434 | - rest.append(dep_phr) | |
435 | - print() | |
436 | - print('--- FIRST:', list(map(str, first))) | |
437 | - print('--- SUBJ:', subj) | |
438 | - print('--- REST:', list(map(str, rest))) | |
439 | - print(typ) | |
454 | + if (words and {'ja', 'ty', 'on', 'my', 'wy'}.intersection(words)) or type(dep_phrase) in (NP, LexQub): | |
455 | + pron.append(dep_phr) | |
456 | + else: | |
457 | + rest.append(dep_phr) | |
458 | + assert (not realisations) | |
459 | + #print() | |
460 | + #print('--- FIRST:', list(map(str, first))) | |
461 | + #print('--- SUBJ:', subj) | |
462 | + #print('--- REST:', list(map(str, rest))) | |
463 | + #print(typ) | |
440 | 464 | assert (len(first) == 1 or typ not in ('int',)) |
441 | - print() | |
442 | - deps1 = [d[1] for d in first] | |
465 | + #print() | |
466 | + deps1 = [d[1] for d in first] + [d[1] for d in pron] | |
443 | 467 | if subj and subj not in first + rest: |
444 | 468 | deps1.append(subj[1]) |
445 | 469 | deps2 = [d[1] for d in rest] |
446 | - # TODO: always ter? sg if no subj? | |
447 | - # TODO separate numbers for subject realisations | |
470 | + # TODO: always ter? sg/m1 if no subj? | |
471 | + # TODO separate numbers/genders for subject realisations? | |
448 | 472 | subj_num = 'sg' |
449 | 473 | if subj and hasattr(subj[0], '_number'): |
450 | 474 | subj_num = correct_num('', subj[0]._number) |
451 | - feats = ['fin', subj_num, 'ter'] | |
475 | + if typ != 'jakby': | |
476 | + feats = ['fin', subj_num, 'ter'] | |
477 | + else: | |
478 | + subj_gend = 'm1' | |
479 | + if subj: | |
480 | + subj_gends = set(get_gender(w)[0] for w in subj[0]._words._lemmas) | |
481 | + assert (len(subj_gends) == 1) | |
482 | + subj_gend = subj_gends.pop() | |
483 | + feats = ['praet', subj_num, subj_gend] | |
452 | 484 | phrs = [] |
453 | 485 | for lemma in phrase._words._lemmas: |
454 | 486 | for dps1 in product(*deps1): |
455 | 487 | for dps2 in product(*deps2): |
456 | 488 | verb_form = get_form(lemma, feats)[0] |
457 | 489 | phrs.append('{}{}{}{}{}{}{}{}{}'.format(to, comp, ' '.join(dps1), ' ' if dps1 else '', sie, neg, verb_form, ' ' if dps2 else '', ' '.join(dps2))) |
458 | - #for phr in phrs: | |
459 | - # print(' ===>', phr) | |
490 | + for phr in phrs: | |
491 | + print(' ===>', phr) | |
460 | 492 | return phrs |
461 | 493 | if ptype in (XP, AdvP): |
462 | 494 | if phrase._category._limitations: |
... | ... | @@ -511,11 +543,15 @@ def make_modified_phrases(phrase, head, head_type, function, negativity, mod_att |
511 | 543 | for mod_ptype, mod in mod_list: |
512 | 544 | texts.append(build_phrase(head, '({})'.format(mod), head_type, mod_ptype)) |
513 | 545 | elif phrase._modification._atr == 'ratr': |
514 | - for mod_list2 in powerset_nonempty(mod_list): | |
515 | - p = head | |
516 | - for mod_ptype, mod in mod_list2: | |
517 | - p = build_phrase(p, '{}'.format(mod), head_type, mod_ptype) | |
518 | - texts.append(p) | |
546 | + #for mod_list2 in powerset_nonempty(mod_list): | |
547 | + # p = head | |
548 | + # for mod_ptype, mod in mod_list2: | |
549 | + # p = build_phrase(p, '{}'.format(mod), head_type, mod_ptype) | |
550 | + # texts.append(p) | |
551 | + p = head | |
552 | + for mod_ptype, mod in mod_list: | |
553 | + p = build_phrase(p, '{}'.format(mod), head_type, mod_ptype) | |
554 | + texts.append(p) | |
519 | 555 | elif phrase._modification._atr == 'atr': |
520 | 556 | p = head |
521 | 557 | for mod_ptype, mod in mod_list: |
... | ... |
entries/phrase_descriptions/morph_generation.py
entries/phrase_descriptions/utils.py
... | ... | @@ -39,7 +39,7 @@ def build_phrase(head, dep, head_type, dep_type): |
39 | 39 | order = POST |
40 | 40 | if head_type == Qub: |
41 | 41 | if dep_type in (LexQub,): |
42 | - return PRE | |
42 | + order = PRE | |
43 | 43 | if order == PRE: |
44 | 44 | return '{} {}'.format(dep, head) |
45 | 45 | if order == POST: |
... | ... | @@ -147,9 +147,12 @@ def correct_sie(sie): |
147 | 147 | |
148 | 148 | def correct_feats(lemma, feats, praep=False): |
149 | 149 | if lemma == 'on': |
150 | - return feats + ['m1', 'akc', 'praep' if praep else 'npraep'] | |
150 | + | |
151 | + return feats + ['m1', 'nakc', 'praep' if praep else 'npraep'] | |
151 | 152 | if lemma in ('ja', 'ty',): |
152 | - return feats + ['m1', ['akc', '']] | |
153 | + # mi, ci, cię | |
154 | + akc = 'nakc' if 'dat' in feats or ({'acc', 'gen'}.intersection(feats) and lemma == 'ty') else 'akc' | |
155 | + return feats + ['m1', [akc, '']] | |
153 | 156 | if lemma in ('my', 'wy'): |
154 | 157 | return feats + ['m1'] |
155 | 158 | if lemma == 'oba': |
... | ... | @@ -162,7 +165,7 @@ def get_subst_attrs(lemma, tag): |
162 | 165 | return { 'case' : feats[1] } |
163 | 166 | return {'num': feats[1], 'case': feats[2], 'gend' : feats[3]} |
164 | 167 | |
165 | -def get_gender_for_num(lemma): | |
168 | +def get_gender(lemma): | |
166 | 169 | form = get_form(lemma, ['subst', 'sg', 'nom']) |
167 | 170 | # 1 or 2 values: ['f'], ['n', 'ncol'], ... |
168 | 171 | gend = form[1].split(':')[3:] |
... | ... | @@ -190,5 +193,6 @@ def get_forms(lemma, feats): |
190 | 193 | except: |
191 | 194 | pass |
192 | 195 | if ret: |
196 | + #print('get_forms', lemma, feats, ret) | |
193 | 197 | return ret |
194 | 198 | 1/0 |
... | ... |
entries/polish_strings.py
... | ... | @@ -32,3 +32,55 @@ FRAME_OPINION = { |
32 | 32 | 'met' : 'metaforyczna', |
33 | 33 | 'unk' : 'nieznana', |
34 | 34 | } |
35 | + | |
36 | +PHRASE_ATTRIBUTE = { | |
37 | + 'case' : 'Przypadek', | |
38 | + 'num' : 'Liczba', | |
39 | + 'gend' : 'Rodzaj', | |
40 | + 'deg' : 'Stopień', | |
41 | + 'prep' : 'Przyimek', | |
42 | + 'comprep' : 'Przyimek złożony', | |
43 | + 'inhsie' : 'Inherentne <i>się</i>', | |
44 | + 'cptype' : 'Typ frazy zdaniowej', | |
45 | + 'cpreals' : 'Realizacje', | |
46 | + 'aspect' : 'Aspekt', | |
47 | + 'advcat' : 'Typ okolicznika', | |
48 | + 'comparcat' : 'Typ frazy porównawczej', | |
49 | + 'text' : 'Postać frazeologizmu', | |
50 | +} | |
51 | + | |
52 | +PHRASE_TYPE = { | |
53 | + 'adjp' : 'przymiotnikowa', | |
54 | + 'advp' : 'przysłówkowa', | |
55 | + 'compar' : 'porównawcza', | |
56 | + 'comprepnp' : 'przyimkowa z przyimkiem złożonym', | |
57 | + 'cp' : 'zdaniowa', | |
58 | + 'distrp' : 'dystrybutywna', | |
59 | + # TODO | |
60 | + 'E' : 'podmiot czasownika wymagającego bezokolicznika', | |
61 | + 'fixed' : 'frazeologizm zamrożony', | |
62 | + 'infp' : 'bezokolicznikowa', | |
63 | + 'lex' : 'frazeologizm', | |
64 | + 'ncp' : 'zdaniowa z korelatem', | |
65 | + 'nonch' : 'niechromatyczna', | |
66 | + 'np' : 'rzeczownikowa', | |
67 | + 'nump' : 'liczebnikowa', | |
68 | + 'or' : 'mowa niezależna', | |
69 | + 'possp' : 'posesywna', | |
70 | + 'ppasp' : 'imiesłowowa', | |
71 | + 'prepadjp' : 'przyimkowo-przymotnikowa', | |
72 | + 'prepgerp' : 'przyimkowo-odsłownikowa', | |
73 | + 'prepncp' : 'zdaniowa z korelatem przyimkowym', | |
74 | + 'prepnp' : 'przyimkowo-rzeczownikowa', | |
75 | + 'prepnump' : 'przyimkowo-liczebnikowa', | |
76 | + 'prepppasp' : 'przyimkowo-imiesłowowa', | |
77 | + 'qub' : 'partykuła', | |
78 | + 'recip' : 'wzajemnościowa partykuła się', | |
79 | + 'refl' : 'zwrotna partykuła się', | |
80 | + 'xp' : 'okolicznikowa', | |
81 | +} | |
82 | + | |
83 | +# phrase types that should not be displayed as “Fraza xyz”, but “Xyz” | |
84 | +NO_PHRASE_NAME = ( | |
85 | + 'E', 'fixed', 'lex', 'or', 'qub', 'recip', 'refl', | |
86 | +) | |
... | ... |
entries/static/entries/js/entries.js
... | ... | @@ -4,6 +4,10 @@ var curr_entry = null; |
4 | 4 | |
5 | 5 | var syntax_state = { curr_schema : null, curr_example : null, curr_position : null, curr_phrase: null } |
6 | 6 | |
7 | +jQuery.fn.tagName = function() { | |
8 | + return this.prop('tagName').toLowerCase(); | |
9 | +}; | |
10 | + | |
7 | 11 | function show_debug(text) { |
8 | 12 | $('#debug').html(text); |
9 | 13 | } |
... | ... | @@ -62,55 +66,231 @@ function show_form_errors(errors) { |
62 | 66 | } |
63 | 67 | } |
64 | 68 | |
65 | -// data_object: schema/frame | |
66 | -/*function make_table(data_object, data_name, label1, label2, items_name, subitem_name1, subitem_name2, linked_item_name) { | |
67 | - var div = document.createElement('div'); | |
68 | - div.className = data_name; | |
69 | - var table = document.createElement('table'); | |
70 | - table.className = 'table table-borderless border border-primary'; | |
71 | - var tbody = document.createElement('tbody'); | |
72 | - | |
73 | - // opinion | |
74 | - var opinion_row = document.createElement('tr'); | |
75 | - opinion_row.innerHTML = '<td class="py-2">Opinia:</td>'; | |
76 | - opinion_row.innerHTML += '<td class="py-2">[' + data_object.id + '] ' + data_object.opinion + '</td>'; | |
77 | - | |
78 | - // functions and phrase types / roles and selectional preferences | |
79 | - var row1 = document.createElement('tr'); | |
80 | - row1.innerHTML = '<td class="py-2">' + label1 + ':</td>'; | |
81 | - var row2 = document.createElement('tr'); | |
82 | - row2.innerHTML = '<td class="py-2">' + label2 + ':</td>'; | |
83 | - // don’t append to row2.innerHTML since it automatically closes tags *** (?) | |
84 | - var row2_html = ''; | |
85 | - for (var i in data_object[items_name]) { | |
86 | - var item = data_object[items_name][i]; | |
87 | - row1.innerHTML += '<td class="py-2 px-1 border-top border-left border-primary">' + item[subitem_name1] + '</td>'; | |
88 | - row2_html += '<td class="px-1 border-top border-left border-primary text-break">'; | |
89 | - for (var j in item[subitem_name2 + 's']) { | |
90 | - var subitem = item[subitem_name2 + 's'][j]; | |
91 | - var cls = j > 0 ? ' border-top border-primary' : ''; | |
92 | - for (var k in subitem[linked_item_name + 's']) { | |
93 | - cls += ' ' + linked_item_name + '-' + subitem[linked_item_name + 's'][k]; | |
69 | +function bind_add_subforms(selector) { | |
70 | + selector.find('.add-button').click(function() { | |
71 | + add_subform($(this)); | |
72 | + }); | |
73 | +} | |
74 | + | |
75 | +function bind_remove_subforms(selector) { | |
76 | + selector.find('.remove-button').mouseenter(function() { | |
77 | + $(this).closest('.subform').addClass('to-remove').find('.subform').addClass('to-remove'); | |
78 | + }).mouseleave(function() { | |
79 | + $(this).closest('.subform').removeClass('to-remove').find('.subform').removeClass('to-remove'); | |
80 | + }).click(function() { | |
81 | + var to_remove = $(this).closest('form'); | |
82 | + // there is an ‘or’ after this form, but no form before it: | |
83 | + // removing the form would leave an ‘or’ with no preceding form, | |
84 | + // therefore remove the ‘or’ as well | |
85 | + if (to_remove.next().hasClass('form-or') && !to_remove.prev().hasClass('subform')) { | |
86 | + to_remove.next().remove(); | |
87 | + } | |
88 | + $(this).closest('form').remove(); | |
89 | + }); | |
90 | +} | |
91 | + | |
92 | +function bind_ors(selector) { | |
93 | + selector.find('.or-button').click(function() { | |
94 | + // if there is no subform yet, nothing will be inserted | |
95 | + // prevAll: the elements are returned in order beginning with the closest sibling | |
96 | + var insert_after = $(this).prevAll('.subform').first(); | |
97 | + // if there is already an ‘or’ there, nothing will be inserted | |
98 | + if (insert_after.next().hasClass('form-or')) { | |
99 | + return; | |
100 | + } | |
101 | + insert_after.after('<div class="form-or mb-1 ml-3"><span class="btn btn-primary btn-sm disabled">LUB</span><button type="button" class="btn remove-or-button btn-sm btn-warning">Usuń</div>'); | |
102 | + selector.find('.remove-or-button').mouseenter(function() { | |
103 | + $(this).closest('div').addClass('to-remove'); | |
104 | + }).mouseleave(function() { | |
105 | + $(this).closest('div').removeClass('to-remove'); | |
106 | + }).click(function() { | |
107 | + $(this).closest('div').remove(); | |
108 | + }); | |
109 | + }); | |
110 | +} | |
111 | + | |
112 | +function bind_autocompletes(selector) { | |
113 | + selector.find('.regex-autocomplete').each(function() { | |
114 | + var autocompleted = $(this); | |
115 | + autocompleted.autocomplete({ | |
116 | + source: function(request, autocompleter) { | |
117 | + $.ajax({ | |
118 | + type : 'get', | |
119 | + url : '/entries/autocomplete/', | |
120 | + dataType : 'json', | |
121 | + data : {'what' : autocompleted.data('autocomplete'), 'text' : request.term}, | |
122 | + success : function(response) { | |
123 | + autocompleter(response.suggestions); | |
124 | + }, | |
125 | + error: function(request, errorType, errorMessage) { | |
126 | + show_error(); | |
127 | + show_debug(errorType + ' (' + errorMessage + ')'); | |
128 | + } | |
129 | + }); | |
94 | 130 | } |
95 | - row2_html += '<div class="' + subitem_name2 + ' py-2' + cls + '" data-' + subitem_name2 + '_id="' + subitem.id + '">' + subitem.str + '</div>'; | |
131 | + }); | |
132 | + }); | |
133 | +} | |
134 | + | |
135 | +function bind_clickable_labels(selector) { | |
136 | + // the labels are clickable, so show it to the user by highlighting them on mouseover | |
137 | + selector.find('.custom-control-label').mouseenter(function() { | |
138 | + $(this).addClass('text-primary'); | |
139 | + }).mouseleave(function() { | |
140 | + $(this).removeClass('text-primary'); | |
141 | + }); | |
142 | +} | |
143 | + | |
144 | +function bind_form_events(selector) { | |
145 | + bind_add_subforms(selector); | |
146 | + bind_remove_subforms(selector); | |
147 | + bind_ors(selector); | |
148 | + bind_autocompletes(selector); | |
149 | + bind_clickable_labels(selector); | |
150 | +} | |
151 | + | |
152 | +// inserter: a callback that inserts the subform HTML into the DOM | |
153 | +// and returns the inserted element containing the form | |
154 | +function insert_subform(subform_type, inserter) { | |
155 | + $.ajax({ | |
156 | + // TODO? this is not very nice, but we we want to wait for the form | |
157 | + // to be inserted into the DOM before doing further stuff with it | |
158 | + async : false, | |
159 | + type : 'get', | |
160 | + url : '/entries/get_subform/', | |
161 | + data : {'subform_type' : subform_type}, | |
162 | + dataType : 'json', | |
163 | + success : function(response) { | |
164 | + var inserted = inserter(response.form_html); | |
165 | + inserted.addClass('subform'); | |
166 | + bind_form_events(inserted); | |
167 | + }, | |
168 | + error: function(request, errorType, errorMessage) { | |
169 | + show_error(); | |
170 | + show_debug(errorType + ' (' + errorMessage + ')'); | |
96 | 171 | } |
97 | - // *** and we want to close the <td> here | |
98 | - row2_html += '</td>'; | |
99 | - } | |
100 | - row2.innerHTML += row2_html; | |
172 | + }); | |
173 | +} | |
174 | + | |
175 | +function initialize_main_form() { | |
176 | + | |
177 | + /* | |
178 | + // accordion variant – only one panel expanded at one time | |
179 | + //$('#main-form').prepend('<div class="accordion" id="main-form-accordion"></div>'); | |
180 | + // independent collapsibles variant – any cobination of panels expanded at one time | |
181 | + $('#main-form').prepend('<div id="main-form-accordion"></div>'); | |
182 | + var inserter = function(form_type, header, expanded) { | |
183 | + return function(form_html) { | |
184 | + $('#main-form-accordion').append( | |
185 | + '<div class="card">\ | |
186 | + <div class="card-header" id="heading-form-' + form_type + '"><h2 class="mb-0">\ | |
187 | + <button class="btn btn-link' + (expanded ? '' : ' collapsed') + '" type="button" data-toggle="collapse" data-target="#collapse-form-' + form_type + '" aria-expanded="' + (expanded ? 'true' : 'false') +'" aria-controls="collapse=form-' + form_type + '">' + | |
188 | + header + | |
189 | + ' [<span class="collapse-form-state">' + (expanded ? 'Zwiń' : 'Rozwiń') +'</span>]' + | |
190 | + '</button></h2>\ | |
191 | + </div>\ | |
192 | + <div id="collapse-form-' + form_type + '" class="collapse form-collapse' + (expanded ? ' show' : '') + '" aria-labelledby="heading-form-' + form_type + | |
193 | + // uncomment for accordion variant (all other collapsibles under this parent will be collapsed on expansion) | |
194 | + //'" data-parent="#main-form-accordion">' + | |
195 | + '<div class="card-body" id="main-form-' + form_type + '">' + | |
196 | + form_html + | |
197 | + '</div>\ | |
198 | + </div>\ | |
199 | + </div>') | |
200 | + return $('#main-form-accordion').children().last(); | |
201 | + }; | |
202 | + }; | |
101 | 203 | |
102 | - tbody.append(opinion_row); | |
103 | - tbody.append(row1); | |
104 | - tbody.append(row2); | |
105 | - table.append(tbody); | |
106 | - div.append(table); | |
204 | + //insert_subform('entry', inserter('entry', 'Po właściwościach haseł', false)); | |
205 | + //insert_subform('schema', inserter('schema', 'Po właściwościach schematów', true)); | |
206 | + insert_subform('schema', inserter('schema', 'Po właściwościach schematów', false)); | |
207 | + //insert_subform('frame', inserter('frame', 'Po właściwościach ram', true)); | |
208 | + insert_subform('frame', inserter('frame', 'Po właściwościach ram', false)); | |
107 | 209 | |
108 | - return div; | |
210 | + $('.form-collapse').on('show.bs.collapse', function() { | |
211 | + $(this).prev().find('.collapse-form-state').html('Zwiń'); | |
212 | + }).on('hide.bs.collapse', function(){ | |
213 | + $(this).prev().find('.collapse-form-state').html('Rozwiń'); | |
214 | + }); | |
215 | + */ | |
216 | + | |
217 | + bind_form_events($('#main-form')); | |
218 | + | |
219 | + $('#main-form').data('depth', '0'); | |
220 | + | |
221 | + $('#main-form').submit(function(event) { | |
222 | + var submit = $(this).find('#submit-id-main-submit'); | |
223 | + submit.prop('disabled', true); | |
224 | + event.preventDefault(); | |
225 | + clear_results(); | |
226 | + clear_debug(); | |
227 | + show_spinner(); | |
228 | + hide_form_errors(); | |
229 | + var data = { 'forms' : serialize_forms($(this)) }; | |
230 | + $.ajax({ | |
231 | + type : 'post',//$(this).attr('method'), | |
232 | + url : $(this).attr('action'), | |
233 | + dataType : 'json', | |
234 | + data : data, | |
235 | + timeout : 60000, | |
236 | + success : function(response) { | |
237 | + show_debug(response.debug); | |
238 | + clear_info(); | |
239 | + if (response.errors) { | |
240 | + show_form_errors(response.errors); | |
241 | + } | |
242 | + show_entries(response.result); | |
243 | + submit.prop('disabled', false); | |
244 | + // TODO uncomment | |
245 | + $('#entry-filters').modal('hide'); | |
246 | + //show_filters(); | |
247 | + }, | |
248 | + error: function(request, errorType, errorMessage) { | |
249 | + show_error(); | |
250 | + show_debug(errorType + ' (' + errorMessage + ')'); | |
251 | + submit.prop('disabled', false); | |
252 | + } | |
253 | + }); | |
254 | + }); | |
255 | + | |
256 | + $('#reset-id-main-reset').click(function(event) { | |
257 | + // in addition to resetting the form, remove all subforms and ors | |
258 | + $('#main-form').find('.subform').remove(); | |
259 | + $('#main-form').find('.form-or').remove(); | |
260 | + }); | |
109 | 261 | } |
110 | 262 | |
111 | -function schema2dom(schema) { | |
112 | - return make_table(schema, 'schema', 'Funkcja', 'Typy fraz', 'positions', 'func', 'phrase', 'argument'); | |
113 | -}*/ | |
263 | +function add_subform(button) { | |
264 | + var depth = parseInt(button.closest('form').data('depth')) + 1; | |
265 | + if (depth > 10) { | |
266 | + // TODO | |
267 | + alert('Może nie przesadzajmy z tym zagnieżdżaniem?'); | |
268 | + return; | |
269 | + } | |
270 | + var subform_type = button.data('add'); | |
271 | + var insert_before = button; | |
272 | + // check the coupled input for subform type | |
273 | + if (subform_type === 'switch') { | |
274 | + subform_type = button.closest('.input-group').find('select').val(); | |
275 | + // nothing was chosen -> do nothing | |
276 | + if (subform_type === '') { | |
277 | + return; | |
278 | + } | |
279 | + var prefix = button.data('prefix'); | |
280 | + if (typeof prefix !== 'undefined') { | |
281 | + subform_type = prefix + subform_type; | |
282 | + } | |
283 | + insert_before = button.closest('.form-group'); | |
284 | + } | |
285 | + insert_subform(subform_type, function(form_html) { | |
286 | + insert_before.before(form_html); | |
287 | + var inserted = insert_before.prev(); | |
288 | + //inserted.addClass('border-left border-bottom border-dark py-1 pl-1 mb-1 ml-3 form-depth-' + depth); | |
289 | + inserted.addClass('p-1 mb-1 ml-3 form-depth-' + depth); | |
290 | + inserted.data('depth', depth) | |
291 | + return inserted; | |
292 | + }); | |
293 | +} | |
114 | 294 | |
115 | 295 | function make_opinion_row(item, span, width) { |
116 | 296 | var opinion_row = document.createElement('tr'); |
... | ... | @@ -124,6 +304,7 @@ function tooltipped_span(text, tooltip_text) { |
124 | 304 | return '<span data-toggle="tooltip" data-placement="bottom"' + html + ' title="' + tooltip_text +'">' + text + '</span>'; |
125 | 305 | } |
126 | 306 | |
307 | +// TODO make schema2dom and frame2dom share as much code as possible | |
127 | 308 | function schema2dom(schema) { |
128 | 309 | var div = document.createElement('div'); |
129 | 310 | div.className = 'schema'; |
... | ... | @@ -197,8 +378,8 @@ function subentries2dom(subentries) { |
197 | 378 | } |
198 | 379 | fragment.append(subentry_div); |
199 | 380 | } |
200 | - if (fragment.childNodes.length == 0) { | |
201 | - fragment.innerHTML = '<p>(brak schematów)</p>'; | |
381 | + if (fragment.childNodes.length === 0) { | |
382 | + return '<p>(brak schematów)</p>'; | |
202 | 383 | } |
203 | 384 | return fragment; |
204 | 385 | } |
... | ... | @@ -208,10 +389,10 @@ function show_syntax(subentries) { |
208 | 389 | $('#syntax').append($(schemata_dom)); |
209 | 390 | |
210 | 391 | $('#syntax').find('.schema').mouseenter(function() { |
211 | - $(this).addClass('bg-lightgray'); | |
392 | + $(this).addClass('bg-red'); | |
212 | 393 | show_debug('Kliknięcie na schemat spowoduje wyświetlenie przykładów.'); |
213 | 394 | }).mouseleave(function() { |
214 | - $(this).removeClass('bg-lightgray'); | |
395 | + $(this).removeClass('bg-red'); | |
215 | 396 | clear_debug(); |
216 | 397 | }); |
217 | 398 | } |
... | ... | @@ -267,66 +448,30 @@ function frames2dom(frames) { |
267 | 448 | fragment.append(frame2dom(frames[i])); |
268 | 449 | } |
269 | 450 | if (fragment.childNodes.length == 0) { |
270 | - fragment.innerHTML = '<p>(brak ram)</p>'; | |
451 | + return '<p>(brak ram)</p>'; | |
271 | 452 | } |
272 | 453 | return fragment; |
273 | 454 | } |
274 | 455 | |
275 | 456 | function show_semantics(frames, subentries) { |
276 | - /*var html = ''; | |
277 | - for (var i in frames) { | |
278 | - var frame = frames[i] | |
279 | - html += '<div class="border border-primary px-3 mb-3">'; | |
280 | - html += '<p class="mb-0">[' + frame.id + '] ' + frame.opinion + '</p>'; | |
281 | - html += '<div class="row">'; | |
282 | - for (var j in frame.arguments) { | |
283 | - var argument = frame.arguments[j]; | |
284 | - var cls = ''; | |
285 | - for (var k in argument.phrases) { | |
286 | - cls += ' phrase-' + argument.phrases[k]; | |
287 | - } | |
288 | - html += '<div class="argument col m-1 p-1 border' + cls + '" data-argument_id="' + argument.id + '">' | |
289 | - html += '<p>' + argument.str + '</p>'; | |
290 | - for (var k in argument.preferences) { | |
291 | - html += '<div class="p-1 border">' + argument.preferences[k] + '</div>'; | |
292 | - } | |
293 | - html += '</div>'; | |
294 | - } | |
295 | - html += '</div>'; | |
296 | - html += '</div>'; | |
297 | - } | |
298 | - if (html != '') { | |
299 | - $('#semantics-frames').html(html); | |
300 | - } else { | |
301 | - $('#semantics-frames').html('<p>(brak ram)</p>'); | |
302 | - }*/ | |
303 | - | |
304 | 457 | var schemata_dom = subentries2dom(subentries); |
305 | 458 | $('#semantics-schemata').append($(schemata_dom)); |
306 | 459 | var frames_dom = frames2dom(frames); |
307 | 460 | $('#semantics-frames').append($(frames_dom)); |
308 | - | |
309 | - /*$('.argument').mouseenter(function () { | |
310 | - $(this).addClass('bg-info text-dark'); | |
311 | - $('.argument-' + $(this).data('argument_id')).addClass('bg-success text-dark'); | |
312 | - }); | |
313 | - $('.argument').mouseleave(function () { | |
314 | - $(this).removeClass('bg-info text-dark'); | |
315 | - $('.argument-' + $(this).data('argument_id')).removeClass('bg-success text-dark'); | |
316 | - });*/ | |
317 | 461 | } |
318 | 462 | |
319 | 463 | function get_schemata_and_frames(entry_id) { |
320 | 464 | clear_entry(); |
321 | 465 | show_spinner(); |
322 | 466 | // TODO should this really be done like this? |
323 | - var data = $('#filters-form').serialize(); | |
324 | - data += '&entry=' + entry_id; | |
467 | + //var data = $('#filters-form').serialize(); | |
468 | + //data += '&entry=' + entry_id; | |
469 | + var data = { 'forms' : serialize_forms($('#main-form')), 'entry' : entry_id }; | |
325 | 470 | $.ajax({ |
326 | - type : 'get', | |
471 | + type : 'post', | |
327 | 472 | url : '/entries/get_entry/', |
328 | 473 | dataType : 'json', |
329 | - data : data, | |
474 | + data : data, | |
330 | 475 | timeout : 60000, |
331 | 476 | success : function(response) { |
332 | 477 | show_syntax(response.subentries); |
... | ... | @@ -376,43 +521,33 @@ function clear_entry() { |
376 | 521 | $('#semantics-schemata').empty(); |
377 | 522 | } |
378 | 523 | |
524 | +function serialize_forms(element) { | |
525 | + var children = []; | |
526 | + element.children().each(function() { | |
527 | + children = children.concat(serialize_forms($(this))); | |
528 | + }); | |
529 | + var tag = element.tagName(); | |
530 | + if (tag === 'form') { | |
531 | + var ret = []; | |
532 | + ret.push(JSON.stringify({'form' : element.serialize(), 'formtype' : element.data('formtype'), 'formnumber' : element.data('formnumber'), 'children' : children})); | |
533 | + return ret; | |
534 | + } else if (element.hasClass('form-or')) { | |
535 | + var ret = []; | |
536 | + ret.push(JSON.stringify({'formtype' : 'or'})); | |
537 | + return ret; | |
538 | + } else if (element.hasClass('and-or-forms')) { | |
539 | + return [JSON.stringify({'formtype' : element.data('formtype'), 'subforms' : children})]; | |
540 | + } | |
541 | + else { | |
542 | + return children; | |
543 | + } | |
544 | +} | |
545 | + | |
379 | 546 | $(document).ready(function() { |
380 | 547 | |
381 | 548 | clear_info(); |
382 | 549 | clear_results(); |
383 | 550 | |
384 | - $('#filters-form').submit(function(event) { | |
385 | - var submit = $(this).find('#submit-id-filters-submit'); | |
386 | - submit.prop('disabled', true); | |
387 | - event.preventDefault(); | |
388 | - clear_results(); | |
389 | - clear_debug(); | |
390 | - show_spinner(); | |
391 | - hide_form_errors(); | |
392 | - $.ajax({ | |
393 | - type : $(this).attr('method'), | |
394 | - url : $(this).attr('action'), | |
395 | - dataType : 'json', | |
396 | - data : $(this).serialize(), | |
397 | - timeout : 60000, | |
398 | - success : function(response) { | |
399 | - show_debug(response.debug); | |
400 | - clear_info(); | |
401 | - if (response.errors) { | |
402 | - show_form_errors(response.errors); | |
403 | - } | |
404 | - show_entries(response.result); | |
405 | - submit.prop('disabled', false); | |
406 | - //show_filters(); | |
407 | - }, | |
408 | - error: function(request, errorType, errorMessage) { | |
409 | - show_error(); | |
410 | - show_debug(errorType + ' (' + errorMessage + ')'); | |
411 | - submit.prop('disabled', false); | |
412 | - } | |
413 | - }); | |
414 | - }); | |
415 | - | |
416 | 551 | $('#id_filter_schema_').change(function() { |
417 | 552 | get_schemata_and_frames(curr_entry); |
418 | 553 | }); |
... | ... | @@ -421,6 +556,10 @@ $(document).ready(function() { |
421 | 556 | get_schemata_and_frames(curr_entry); |
422 | 557 | }); |
423 | 558 | |
424 | - $('#filters-form').submit(); | |
559 | + initialize_main_form(); | |
560 | + | |
561 | + $('#main-form').submit(); | |
562 | + | |
563 | + //$('#filter-button').click(); | |
425 | 564 | |
426 | 565 | }); |
... | ... |
entries/templates/entries.html
... | ... | @@ -6,11 +6,13 @@ |
6 | 6 | {% block title %}Hasła{% endblock %} |
7 | 7 | |
8 | 8 | {% block styles %} |
9 | - <!--link rel="stylesheet" type="text/css" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/themes/smoothness/jquery-ui.css"--> | |
9 | + <!-- for autocomplete --> | |
10 | + <link rel="stylesheet" type="text/css" href="//code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css"> | |
10 | 11 | <!--link rel="stylesheet" type="text/css" href="{% static 'entries/css/panels.css' %}"--> |
11 | 12 | {% endblock %} |
12 | 13 | |
13 | 14 | {% block scripts %} |
15 | + <script src="{% static 'common/js/csrf.js' %}"></script> | |
14 | 16 | <!--script src="{% static 'entries/js/panels.js' %}"></script--> |
15 | 17 | <script src="{% static 'entries/js/entries.js' %}"></script> |
16 | 18 | {% endblock %} |
... | ... | @@ -30,6 +32,9 @@ |
30 | 32 | <div class="row h-100"> |
31 | 33 | <!-- left panel: list of entries --> |
32 | 34 | <div id="entries-list" class="col-2 h-100 overflow-auto border-right border-primary"> |
35 | + <button type="button" id='filter-button' class="btn btn-primary" data-toggle="modal" data-target="#entry-filters"> | |
36 | + Filtrowanie | |
37 | + </button> | |
33 | 38 | {% include "entries_list.html" %} |
34 | 39 | </div> |
35 | 40 | |
... | ... | @@ -44,14 +49,24 @@ |
44 | 49 | </div> |
45 | 50 | </div> |
46 | 51 | |
47 | -<div class="row d-none"> | |
48 | - | |
49 | -<div class="card bg-dark text-light"> | |
50 | - <div class="card-body"> | |
51 | - {% crispy filters_form %} | |
52 | +<div class="modal fade" id="entry-filters" tabindex="-1" role="dialog" aria-labelledby="entry-filtersLabel" aria-hidden="true"> | |
53 | + <div class="modal-dialog modal-xl" role="document"> | |
54 | + <div class="modal-content"> | |
55 | + <div class="modal-header"> | |
56 | + <h5 class="modal-title" id="entry-filtersLabel">Filtrowanie haseł</h5> | |
57 | + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |
58 | + <span aria-hidden="true">×</span> | |
59 | + </button> | |
60 | + </div> | |
61 | + <div class="modal-body"> | |
62 | + {% crispy entries_form %} | |
63 | + </div> | |
64 | + <!--div class="modal-footer"> | |
65 | + <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> | |
66 | + <button type="button" class="btn btn-primary">Save changes</button> | |
67 | + </div--> | |
52 | 68 | </div> |
53 | -</div> | |
54 | - | |
69 | + </div> | |
55 | 70 | </div> |
56 | 71 | |
57 | 72 | {% endblock %} |
... | ... |
entries/templates/test.html
0 → 100644
1 | +{% load static %} | |
2 | + | |
3 | +<!doctype html> | |
4 | + | |
5 | +{% load static %} | |
6 | + | |
7 | +<html lang="pl"> | |
8 | +<head> | |
9 | + <meta charset="utf-8"> | |
10 | + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
11 | + <link rel="icon" href="/static/common/favicon.ico"> | |
12 | + <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css"> | |
13 | + <link rel="stylesheet" type="text/css" href="https://bootswatch.com/4/lux/bootstrap.min.css"> | |
14 | + <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> | |
15 | + <!--might be needed for resizeable panels, causes errors if included after Popper--> | |
16 | + <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script> | |
17 | + <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> | |
18 | + <script src="{% static 'entries/js/entries.js' %}"></script> | |
19 | +</head> | |
20 | + | |
21 | +<body> | |
22 | + <input class="regex-autocomplete" data-autocomplete="phrasetype"> | |
23 | +</body> | |
... | ... |
entries/urls.py
1 | 1 | from django.urls import path |
2 | 2 | |
3 | -from . import views | |
3 | +from . import autocompletes, views | |
4 | 4 | |
5 | 5 | app_name = 'entries' |
6 | 6 | |
7 | 7 | urlpatterns = [ |
8 | 8 | path('get_entries/', views.get_entries, name='get_entries'), |
9 | 9 | path('get_entry/', views.get_entry, name='get_entry'), |
10 | + path('get_subform/', views.get_subform, name='get_subform'), | |
11 | + | |
12 | + path('autocomplete/', autocompletes.autocomplete, name='autocomplete'), | |
13 | + | |
10 | 14 | path('test/', views.test, name='test'), |
11 | 15 | path('', views.entries, name='entries'), |
12 | 16 | ] |
... | ... |
entries/views.py
1 | 1 | import datetime |
2 | +import operator | |
2 | 3 | |
3 | 4 | from collections import defaultdict |
5 | +from functools import reduce | |
4 | 6 | from itertools import chain |
5 | 7 | |
6 | -from django.http import JsonResponse | |
8 | +import simplejson | |
9 | + | |
10 | +from django.db.models import Q | |
11 | +from django.http import JsonResponse, QueryDict | |
7 | 12 | from django.shortcuts import render |
13 | +from django.template.context_processors import csrf | |
14 | + | |
15 | +from crispy_forms.utils import render_crispy_form | |
8 | 16 | |
9 | 17 | from connections.models import Entry, Subentry, ArgumentConnection |
10 | 18 | from syntax.models import NaturalLanguageDescription, Schema |
11 | 19 | from semantics.models import Frame |
12 | 20 | |
13 | -from .forms import FiltersForm | |
21 | +from common.decorators import ajax_required | |
22 | + | |
23 | +from .forms import ( | |
24 | + #MainEntryForm, | |
25 | + EntryForm, | |
26 | + SchemaFormFactory, | |
27 | + FrameFormFactory, | |
28 | + PositionFormFactory, | |
29 | + PhraseAttributesFormFactory, | |
30 | + LexFormFactory, | |
31 | + ArgumentFormFactory, | |
32 | + PredefinedPreferenceFormFactory, | |
33 | + RelationalPreferenceFormFactory, | |
34 | + SynsetPreferenceFormFactory, | |
35 | +) | |
36 | + | |
14 | 37 | from .polish_strings import SCHEMA_OPINION, FRAME_OPINION |
15 | 38 | |
16 | -from .phrase_descriptions.descriptions import position_prop_description, phrase_description | |
39 | +from .phrase_descriptions.descriptions import position_prop_description, phrase_description | |
17 | 40 | |
18 | 41 | def test(request): |
19 | - return render(request, 'test.html', { 'filters_form' : FiltersForm() }) | |
42 | + return render(request, 'test.html', {}) | |
20 | 43 | |
21 | 44 | def entries(request): |
22 | - return render(request, 'entries.html', { 'filters_form' : FiltersForm() }) | |
45 | + return render(request, 'entries.html', { 'entries_form' : EntryForm() }) | |
46 | + | |
47 | +FORM_TYPES = { | |
48 | + #'entry-main' : MainEntryForm, | |
49 | + 'entry' : EntryForm, | |
50 | + | |
51 | +} | |
52 | + | |
53 | +FORM_FACTORY_TYPES = { | |
54 | + 'schema' : SchemaFormFactory, | |
55 | + 'position' : PositionFormFactory, | |
56 | + 'phrase_lex' : LexFormFactory, | |
57 | + 'frame' : FrameFormFactory, | |
58 | + 'argument' : ArgumentFormFactory, | |
59 | + 'predefined' : PredefinedPreferenceFormFactory, | |
60 | + 'relational' : RelationalPreferenceFormFactory, | |
61 | + 'synset' : SynsetPreferenceFormFactory, | |
62 | +} | |
63 | + | |
64 | + | |
65 | +def make_form(form_type, data=None, unique_number=None): | |
66 | + print('======== MAKE FORM', form_type) | |
67 | + if form_type in FORM_FACTORY_TYPES: | |
68 | + return FORM_FACTORY_TYPES[form_type].get_form(data=data, unique_number=unique_number) | |
69 | + if form_type in FORM_TYPES: | |
70 | + return FORM_TYPES[form_type](data=data) | |
71 | + elif form_type.startswith('phrase_'): | |
72 | + phrase_type = form_type[7:] | |
73 | + print('================ MAKE PHRASE FORM', phrase_type, unique_number) | |
74 | + return PhraseAttributesFormFactory.get_form(phrase_type, data=data, unique_number=unique_number) | |
75 | + return None | |
23 | 76 | |
77 | + | |
78 | +@ajax_required | |
79 | +def get_subform(request): | |
80 | + if request.method == 'GET': | |
81 | + ctx = {} | |
82 | + ctx.update(csrf(request)) | |
83 | + form_type = request.GET['subform_type'] | |
84 | + form = make_form(form_type) | |
85 | + try: | |
86 | + form_html = render_crispy_form(form, context=ctx) | |
87 | + except: | |
88 | + print('******************', form_type) | |
89 | + raise | |
90 | + return JsonResponse({'form_html' : form_html}) | |
91 | + | |
24 | 92 | #TODO clean this code bordello up |
25 | 93 | |
26 | -def filter_objects(objects, queries): | |
27 | - print('===================================================================') | |
94 | +def filter_objects(objects, queries, tab=''): | |
95 | + #print(tab + '===================================================================') | |
28 | 96 | for query in queries: |
29 | - print('***', query) | |
97 | + #print(tab + '***', query) | |
30 | 98 | objects = objects.filter(query).distinct() |
31 | - print('---------------------------------------------------------------') | |
32 | - print(objects) | |
33 | - print('\n') | |
34 | - print('===================================================================') | |
99 | + #print(tab + '---------------------------------------------------------------') | |
100 | + #print(tab, objects) | |
101 | + #print('\n') | |
102 | + #print(tab + '===================================================================') | |
103 | + return objects.distinct() | |
104 | + | |
105 | + | |
106 | +def collect_forms(forms_json, tab=' '): | |
107 | + data = simplejson.loads(forms_json) | |
108 | + form_type = data['formtype'] | |
109 | + form_number = data.get('formnumber', 0) | |
110 | + if form_type == 'or': | |
111 | + return 'or' | |
112 | + else: | |
113 | + print(tab, 'FORM:', data['form']) | |
114 | + print(tab, 'TYPE:', form_type, 'NUMBER:', form_number) | |
115 | + #print(tab, 'DATA:', data) | |
116 | + query_params = QueryDict(data['form']) | |
117 | + print(tab, 'PARAMS:', query_params) | |
118 | + form = make_form(form_type, data=query_params, unique_number=form_number) | |
119 | + print(tab, 'FORM TYPE:', type(form)) | |
120 | + if not form.is_valid(): | |
121 | + print(form.errors) | |
122 | + # TODO return validation errors | |
123 | + 1/0 | |
124 | + print(tab, '{} CHILDREN GROUP(S)'.format(len(data['children']))) | |
125 | + # a form may have one or more children forms, organised into and-or | |
126 | + # (e.g. an entry form has child schema forms, frame forms etc.) | |
127 | + subform_groups = [] | |
128 | + for subforms_json in data['children']: | |
129 | + subform_group = simplejson.loads(subforms_json) | |
130 | + subform_type, subforms = subform_group['formtype'], subform_group['subforms'] | |
131 | + children = [[]] | |
132 | + for child in subforms: | |
133 | + child_form = collect_forms(child, tab + ' ') | |
134 | + if child_form == 'or': | |
135 | + children.append([]) | |
136 | + else: | |
137 | + children[-1].append(child_form) | |
138 | + subforms = list(filter(None, children)) | |
139 | + if subforms: | |
140 | + subform_groups.append((subform_type, subforms)) | |
141 | + return (form, subform_groups) | |
142 | + | |
143 | + | |
144 | +def get_filtered_objects(forms, initial_objects=None, tab=' '): | |
145 | + form, children = forms | |
146 | + objects = form.model_class.objects.all() if initial_objects is None else initial_objects.all() | |
147 | + queries = form.get_queries() | |
148 | + print(tab, type(form), 'FOR FILTERING:', form.model_class) | |
149 | + objects = filter_objects(objects, queries, tab=tab) | |
150 | + for children_group in children: | |
151 | + object_ids_or = [] | |
152 | + prefixes = set() | |
153 | + for or_children in children_group[1]: | |
154 | + objects_and = form.model_class.objects.all() if initial_objects is None else initial_objects.all() | |
155 | + for child in or_children: | |
156 | + child_form = child[0] | |
157 | + child_objects = get_filtered_objects(child, tab=tab + ' ') | |
158 | + prefix = form.get_child_form_prefix(child_form) | |
159 | + prefixes.add(prefix) | |
160 | + child_ids = [co.id for co in child_objects] | |
161 | + q = Q((prefix, child_ids)) | |
162 | + objects_and = objects_and.filter(q) | |
163 | + object_ids_or.append({o.id for o in objects_and}) | |
164 | + assert(len(prefixes) == 1) | |
165 | + object_ids = reduce(operator.or_, object_ids_or) | |
166 | + objects = objects.filter(id__in=object_ids) | |
35 | 167 | return objects.distinct() |
168 | + | |
169 | + | |
170 | +# forms – an ‘or’ list of ‘and’ lists of forms, the forms are flattened and treated as one ‘or’ list. | |
171 | +# The function is used for filtering out schemata/frames. E.g. if the user chooses entries with a schema | |
172 | +# safisfying X AND a schema satisfying Y, schemata satisfying X OR Y should be displayed (and all other | |
173 | +# schemata should be hidden). | |
174 | +def get_filtered_objects2(objects, forms): | |
175 | + filtered_ids = [{ schema.id for schema in get_filtered_objects(form, initial_objects=objects) } for form in chain.from_iterable(forms)] | |
176 | + filtered_ids = reduce(operator.or_, filtered_ids) | |
177 | + return objects.filter(id__in=filtered_ids) | |
178 | + | |
36 | 179 | |
37 | 180 | # TODO a more efficient loading of results will be implemented, |
38 | 181 | # for now truncate them to speed up testing |
39 | -DEBUG_N = 100 | |
182 | +DEBUG_N = 500 | |
183 | +@ajax_required | |
40 | 184 | def get_entries(request): |
41 | 185 | t1 = datetime.datetime.now() |
42 | - if request.method == 'GET': | |
43 | - form = FiltersForm(request.GET) | |
44 | - if not form.is_valid(): | |
45 | - print(form.errors) | |
46 | - return JsonResponse({ 'result' : [], 'errors' : form.errors }) | |
47 | - if form.is_valid(): | |
48 | - queries = form.get_queries() | |
49 | - t2 = datetime.datetime.now() | |
50 | - entries = filter_objects(Entry.objects.all(), queries) | |
51 | - # TODO remove! – this is for debug | |
52 | - # some entries with an obj,controlee position, >3 frames etc. | |
53 | - #entries = entries.filter(name__in=('dozwalać', 'dozwolić', 'obiecywać')) | |
54 | - entries = entries.filter(subentries__schemata__positions__phrase_types__text_rep__contains='lex') | |
55 | - t3 = datetime.datetime.now() | |
56 | - #entries_list = [(e.id, e.name, e.status.key) for e in entries]#[:500]] | |
57 | - entries_list = list(entries.values_list('id', 'name', 'status__key'))[:DEBUG_N] | |
58 | - t4 = datetime.datetime.now() | |
59 | - s = ' truncated to {}'.format(DEBUG_N) if len(entries_list) == DEBUG_N else '' | |
60 | - debug = '[{} s] [{} results{}] '.format(t2, entries.count(), s) | |
61 | - debug += '[filtering: {:.1} s] '.format((t2 - t1).total_seconds()) | |
62 | - debug += '[processing: {:.1} s] '.format((t4 - t3).total_seconds()) | |
63 | - debug += '[total time: {:.1} s] '.format((t4 - t1).total_seconds()) | |
64 | - return JsonResponse({ 'result' : entries_list, 'debug' : debug }) | |
186 | + if request.method == 'POST': | |
187 | + forms = collect_forms(request.POST['forms[]']) | |
188 | + entries = get_filtered_objects(forms) | |
189 | + # TODO return validation errors | |
190 | + #if not form.is_valid(): | |
191 | + #print(form.errors) | |
192 | + #return JsonResponse({ 'result' : [], 'errors' : form.errors }) | |
193 | + t2 = datetime.datetime.now() | |
194 | + # TODO remove! – this is for debug | |
195 | + # some entries with an obj,controlee position, >3 frames etc. | |
196 | + #entries = entries.filter(name__in=('dozwalać', 'dozwolić', 'obiecywać')) | |
197 | + #entries = entries.filter(subentries__schemata__positions__phrase_types__text_rep__contains='lex') | |
198 | + t3 = datetime.datetime.now() | |
199 | + entries_list = list(entries.values_list('id', 'name', 'status__key'))[:DEBUG_N] | |
200 | + t4 = datetime.datetime.now() | |
201 | + s = ' truncated to {}'.format(DEBUG_N) if len(entries_list) == DEBUG_N else '' | |
202 | + debug = '[{} s] [{} results{}] '.format(t2, entries.count(), s) | |
203 | + debug += '[filtering: {:.1} s] '.format((t2 - t1).total_seconds()) | |
204 | + debug += '[processing: {:.1} s] '.format((t4 - t3).total_seconds()) | |
205 | + debug += '[total time: {:.1} s] '.format((t4 - t1).total_seconds()) | |
206 | + return JsonResponse({ 'result' : entries_list, 'debug' : debug }) | |
65 | 207 | return JsonResponse({}) |
66 | 208 | |
209 | + | |
67 | 210 | def subentry2str(subentry): |
68 | 211 | ret = subentry.entry.name |
69 | 212 | if subentry.inherent_sie.name == 'true': |
... | ... | @@ -79,6 +222,7 @@ def subentry2str(subentry): |
79 | 222 | ret += ' pred.' |
80 | 223 | return ret |
81 | 224 | |
225 | + | |
82 | 226 | def position_prop2dict(prop): |
83 | 227 | return { |
84 | 228 | 'str' : prop.name, |
... | ... | @@ -88,22 +232,8 @@ def position_prop2dict(prop): |
88 | 232 | 'desc' : '', |
89 | 233 | } |
90 | 234 | |
235 | + | |
91 | 236 | def get_phrase_desc(phrase, position, negativity): |
92 | - print('***---***') | |
93 | - print(phrase.text_rep) | |
94 | - print(position.function, position.control, position.pred_control) | |
95 | - print(negativity) | |
96 | - for d in NaturalLanguageDescription.objects.filter( | |
97 | - phrase_str=phrase.text_rep, | |
98 | - #function=position.function, | |
99 | - #control=position.control, | |
100 | - #pred_control=position.pred_control, | |
101 | - #negativity=negativity, | |
102 | - ): | |
103 | - print('---------') | |
104 | - print(d.phrase_str) | |
105 | - print(d.function, d.control, d.pred_control) | |
106 | - print(d.negativity) | |
107 | 237 | return NaturalLanguageDescription.objects.get( |
108 | 238 | phrase_str=phrase.text_rep, |
109 | 239 | function=position.function, |
... | ... | @@ -111,6 +241,7 @@ def get_phrase_desc(phrase, position, negativity): |
111 | 241 | pred_control=position.pred_control, |
112 | 242 | negativity=negativity).description |
113 | 243 | |
244 | + | |
114 | 245 | def schema2dict(schema, phr2arg, negativity): |
115 | 246 | return { |
116 | 247 | 'opinion' : SCHEMA_OPINION[schema.opinion.key], |
... | ... | @@ -132,9 +263,11 @@ def schema2dict(schema, phr2arg, negativity): |
132 | 263 | ], |
133 | 264 | } |
134 | 265 | |
266 | + | |
135 | 267 | def get_prefs_list(argument): |
136 | 268 | prefs = [argument.predefined.all(), argument.synsets.all(), argument.relations.all()] |
137 | - return list(chain(*map(list, prefs))) | |
269 | + return list(chain.from_iterable(map(list, prefs))) | |
270 | + | |
138 | 271 | |
139 | 272 | def frame2dict(frame, arg2phr): |
140 | 273 | return { |
... | ... | @@ -149,11 +282,13 @@ def frame2dict(frame, arg2phr): |
149 | 282 | } for a in frame.argument_set.all() |
150 | 283 | ], |
151 | 284 | } |
285 | + | |
152 | 286 | |
287 | +@ajax_required | |
153 | 288 | def get_entry(request): |
154 | - if request.method == 'GET': | |
155 | - form = FiltersForm(request.GET) | |
156 | - eid = request.GET['entry'] | |
289 | + if request.method == 'POST': | |
290 | + form = EntryForm(request.POST) | |
291 | + eid = request.POST['entry'] | |
157 | 292 | if eid.isdigit() and form.is_valid(): |
158 | 293 | eid = int(eid) |
159 | 294 | entry = Entry.objects.get(id=eid) |
... | ... | @@ -167,20 +302,37 @@ def get_entry(request): |
167 | 302 | phr2arg[phr].add(arg) |
168 | 303 | arg2phr[arg].add(phr) |
169 | 304 | #=================================================================== |
170 | - schema_queries = [] | |
171 | - if form.cleaned_data['filter_schema_']: | |
172 | - schema_queries = form.get_queries('schema') | |
173 | - frame_queries = [] | |
174 | - if form.cleaned_data['filter_frame_']: | |
175 | - frame_queries = form.get_queries('frame') | |
305 | + | |
306 | + entry_form, children_forms = collect_forms(request.POST['forms[]']) | |
307 | + filter_schemata, filter_frames = entry_form.cleaned_data['filter_schemata'], entry_form.cleaned_data['filter_frames'] | |
308 | + if filter_schemata: | |
309 | + schema_forms = [frms[1] for frms in children_forms if frms[0] == 'schema'] | |
310 | + assert (len(schema_forms) <= 1) | |
311 | + if schema_forms: | |
312 | + schema_forms = schema_forms[0] | |
313 | + else: | |
314 | + filter_schemata = False | |
315 | + if filter_frames: | |
316 | + frame_forms = [frms[1] for frms in children_forms if frms[0] == 'frame'] | |
317 | + assert (len(frame_forms) <= 1) | |
318 | + if frame_forms: | |
319 | + frame_forms = frame_forms[0] | |
320 | + else: | |
321 | + filter_frames = False | |
322 | + | |
176 | 323 | subentries = [] |
177 | 324 | for subentry in entry.subentries.all(): |
178 | 325 | schemata = [] |
179 | - for schema in filter_objects(subentry.schemata.all(), schema_queries): | |
326 | + schema_objects = subentry.schemata.all() | |
327 | + if filter_schemata: | |
328 | + schema_objects = get_filtered_objects2(schema_objects, schema_forms) | |
329 | + for schema in schema_objects: | |
180 | 330 | schemata.append(schema2dict(schema, phr2arg, subentry.negativity)) |
181 | 331 | if schemata: |
182 | 332 | subentries.append({ 'str' : subentry2str(subentry), 'schemata' : schemata }) |
183 | - frame_objs = Frame.objects.filter(argument__argument_connections__schema_connections__subentry__entry=entry) | |
184 | - frames = [frame2dict(frame, arg2phr) for frame in filter_objects(frame_objs, frame_queries)] | |
333 | + frame_objects = Frame.objects.filter(argument__argument_connections__schema_connections__subentry__entry=entry) | |
334 | + if filter_frames: | |
335 | + frame_objects = get_filtered_objects2(frame_objects, frame_forms) | |
336 | + frames = [frame2dict(frame, arg2phr) for frame in frame_objects] | |
185 | 337 | return JsonResponse({ 'subentries' : subentries, 'frames' : frames }) |
186 | 338 | return JsonResponse({}) |
... | ... |
importer/Phrase.py
1 | 1 | #! /usr/bin/python |
2 | 2 | # -*- coding: utf-8 -*- |
3 | 3 | |
4 | -from syntax.models import PhraseTypeModel, PhraseType | |
4 | +from importer.PhraseAttributes import get_attributes, get_lex_attributes, empty_attributes | |
5 | +from syntax.models_phrase import PhraseTypeModel, PhraseType | |
6 | + | |
5 | 7 | |
6 | 8 | class Case: |
7 | 9 | |
... | ... | @@ -236,6 +238,7 @@ class NP: |
236 | 238 | phraseologic=False, |
237 | 239 | defaults={'priority': 0}) |
238 | 240 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
241 | + attributes=get_attributes(main_type.name, self), | |
239 | 242 | lexicalized_phrase=None, |
240 | 243 | text_rep=str(self)) |
241 | 244 | position.phrase_types.add(phrase) |
... | ... | @@ -271,9 +274,11 @@ class LexNP: |
271 | 274 | phraseologic=False, |
272 | 275 | defaults={'priority':0}) |
273 | 276 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
277 | + attributes=get_attributes(lex_type.name, self._np), | |
274 | 278 | lexicalized_phrase=None, |
275 | 279 | text_rep=str(self._np)) |
276 | 280 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
281 | + attributes=get_lex_attributes(lex_type.name, self), | |
277 | 282 | lexicalized_phrase=lex, |
278 | 283 | text_rep=str(self)) |
279 | 284 | position.phrase_types.add(phrase) |
... | ... | @@ -288,35 +293,8 @@ class LexNP: |
288 | 293 | |
289 | 294 | def retyped(self, new_type): |
290 | 295 | return 'lex(' + str(new_type) + ',' + self._number + ',' + str(self._words) + ',' + str(self._modification) + ')' |
291 | - | |
292 | - | |
293 | -class AdjP: | |
294 | - | |
295 | - def __init__(self, case, id): | |
296 | - self._case = case | |
297 | - self._id = id | |
298 | - | |
299 | - @classmethod | |
300 | - def fromTree(cls, tree, id): | |
301 | - case = Case(tree._children[0]._children[0]._attrs['value']) | |
302 | - return cls(case, id) | |
303 | - | |
304 | - def getId(self): | |
305 | - return self._id | |
306 | 296 | |
307 | - def store(self, position): | |
308 | - main_type, _ = PhraseTypeModel.objects.get_or_create(name='adjp', | |
309 | - phraseologic=False, | |
310 | - defaults={'priority': 0}) | |
311 | - phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, | |
312 | - lexicalized_phrase=None, | |
313 | - text_rep=str(self)) | |
314 | - position.phrase_types.add(phrase) | |
315 | - | |
316 | - def __str__(self): | |
317 | - return 'adjp(' + str(self._case) + ')' | |
318 | 297 | |
319 | - | |
320 | 298 | class PrepNP: |
321 | 299 | |
322 | 300 | def __init__(self, prep, id): |
... | ... | @@ -337,6 +315,7 @@ class PrepNP: |
337 | 315 | phraseologic=False, |
338 | 316 | defaults={'priority': 0}) |
339 | 317 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
318 | + attributes=get_attributes(main_type.name, self), | |
340 | 319 | lexicalized_phrase=None, |
341 | 320 | text_rep=str(self)) |
342 | 321 | position.phrase_types.add(phrase) |
... | ... | @@ -373,9 +352,11 @@ class LexPrepNP: |
373 | 352 | phraseologic=False, |
374 | 353 | defaults={'priority':0}) |
375 | 354 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
355 | + attributes=get_attributes(lex_type.name, self._prepnp), | |
376 | 356 | lexicalized_phrase=None, |
377 | 357 | text_rep=str(self._prepnp)) |
378 | 358 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
359 | + attributes=get_lex_attributes(lex_type.name, self), | |
379 | 360 | lexicalized_phrase=lex, |
380 | 361 | text_rep=str(self)) |
381 | 362 | position.phrase_types.add(phrase) |
... | ... | @@ -412,6 +393,7 @@ class PrepNumP: |
412 | 393 | phraseologic=False, |
413 | 394 | defaults={'priority': 0}) |
414 | 395 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
396 | + attributes=get_attributes(main_type.name, self), | |
415 | 397 | lexicalized_phrase=None, |
416 | 398 | text_rep=str(self)) |
417 | 399 | position.phrase_types.add(phrase) |
... | ... | @@ -448,8 +430,11 @@ class LexPrepNumP: |
448 | 430 | defaults={'priority': 0}) |
449 | 431 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
450 | 432 | lexicalized_phrase=None, |
433 | + attributes=get_attributes(lex_type.name, self._prepnump), | |
451 | 434 | text_rep=str(self._prepnump)) |
452 | 435 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
436 | + # TODO!!! | |
437 | + attributes=empty_attributes(), | |
453 | 438 | lexicalized_phrase=lex, |
454 | 439 | text_rep=str(self)) |
455 | 440 | position.phrase_types.add(phrase) |
... | ... | @@ -485,6 +470,7 @@ class NumP: |
485 | 470 | phraseologic=False, |
486 | 471 | defaults={'priority': 0}) |
487 | 472 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
473 | + attributes=get_attributes(main_type.name, self), | |
488 | 474 | lexicalized_phrase=None, |
489 | 475 | text_rep=str(self)) |
490 | 476 | position.phrase_types.add(phrase) |
... | ... | @@ -517,13 +503,16 @@ class LexNumP: |
517 | 503 | main_type, _ = PhraseTypeModel.objects.get_or_create(name='lex', |
518 | 504 | phraseologic=True, |
519 | 505 | defaults={'priority': 0}) |
520 | - lex_type, _ = PhraseTypeModel.objects.get_or_create(name='lexnump', | |
506 | + lex_type, _ = PhraseTypeModel.objects.get_or_create(name='nump', | |
521 | 507 | phraseologic=False, |
522 | 508 | defaults={'priority': 0}) |
523 | 509 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
510 | + attributes=get_attributes(lex_type.name, self._nump), | |
524 | 511 | lexicalized_phrase=None, |
525 | 512 | text_rep=str(self._nump)) |
526 | 513 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
514 | + # TODO !!! | |
515 | + attributes=empty_attributes(), | |
527 | 516 | lexicalized_phrase=lex, |
528 | 517 | text_rep=str(self)) |
529 | 518 | position.phrase_types.add(phrase) |
... | ... | @@ -561,6 +550,7 @@ class PrepAdjP: |
561 | 550 | phraseologic=False, |
562 | 551 | defaults={'priority': 0}) |
563 | 552 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
553 | + attributes=get_attributes(main_type.name, self), | |
564 | 554 | lexicalized_phrase=None, |
565 | 555 | text_rep=str(self)) |
566 | 556 | position.phrase_types.add(phrase) |
... | ... | @@ -601,9 +591,11 @@ class LexPrepAdjP: |
601 | 591 | phraseologic=False, |
602 | 592 | defaults={'priority': 0}) |
603 | 593 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
594 | + attributes=get_attributes(lex_type.name, self._prepadjp), | |
604 | 595 | lexicalized_phrase=None, |
605 | 596 | text_rep=str(self._prepadjp)) |
606 | 597 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
598 | + attributes=get_lex_attributes(lex_type.name, self), | |
607 | 599 | lexicalized_phrase=lex, |
608 | 600 | text_rep=str(self)) |
609 | 601 | position.phrase_types.add(phrase) |
... | ... | @@ -639,6 +631,7 @@ class ComPrepNP: |
639 | 631 | phraseologic=False, |
640 | 632 | defaults={'priority': 0}) |
641 | 633 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
634 | + attributes=get_attributes(main_type.name, self), | |
642 | 635 | lexicalized_phrase=None, |
643 | 636 | text_rep=str(self)) |
644 | 637 | position.phrase_types.add(phrase) |
... | ... | @@ -666,6 +659,7 @@ class CP: |
666 | 659 | phraseologic=False, |
667 | 660 | defaults={'priority': 0}) |
668 | 661 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
662 | + attributes = get_attributes(main_type.name, self), | |
669 | 663 | lexicalized_phrase=None, |
670 | 664 | text_rep=str(self)) |
671 | 665 | position.phrase_types.add(phrase) |
... | ... | @@ -707,9 +701,11 @@ class LexCP: |
707 | 701 | phraseologic=False, |
708 | 702 | defaults={'priority': 0}) |
709 | 703 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
704 | + attributes = get_attributes(lex_type.name, self._cp), | |
710 | 705 | lexicalized_phrase=None, |
711 | 706 | text_rep=str(self._cp)) |
712 | 707 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
708 | + attributes = get_lex_attributes(lex_type.name, self), | |
713 | 709 | lexicalized_phrase=lex, |
714 | 710 | text_rep=str(self)) |
715 | 711 | position.phrase_types.add(phrase) |
... | ... | @@ -747,6 +743,7 @@ class NCP: |
747 | 743 | phraseologic=False, |
748 | 744 | defaults={'priority': 0}) |
749 | 745 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
746 | + attributes=get_attributes(main_type.name, self), | |
750 | 747 | lexicalized_phrase=None, |
751 | 748 | text_rep=str(self)) |
752 | 749 | position.phrase_types.add(phrase) |
... | ... | @@ -787,9 +784,11 @@ class LexNCP: |
787 | 784 | phraseologic=False, |
788 | 785 | defaults={'priority': 0}) |
789 | 786 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
787 | + attributes=get_attributes(lex_type.name, self._ncp), | |
790 | 788 | lexicalized_phrase=None, |
791 | 789 | text_rep=str(self._ncp)) |
792 | 790 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
791 | + attributes=get_lex_attributes(lex_type.name, self), | |
793 | 792 | lexicalized_phrase=lex, |
794 | 793 | text_rep=str(self)) |
795 | 794 | position.phrase_types.add(phrase) |
... | ... | @@ -828,6 +827,7 @@ class PrepNCP: |
828 | 827 | phraseologic=False, |
829 | 828 | defaults={'priority': 0}) |
830 | 829 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
830 | + attributes=get_attributes(main_type.name, self), | |
831 | 831 | lexicalized_phrase=None, |
832 | 832 | text_rep=str(self)) |
833 | 833 | position.phrase_types.add(phrase) |
... | ... | @@ -853,6 +853,7 @@ class Nonch: |
853 | 853 | phraseologic=False, |
854 | 854 | defaults={'priority': 0}) |
855 | 855 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
856 | + attributes=empty_attributes(), | |
856 | 857 | lexicalized_phrase=None, |
857 | 858 | text_rep=str(self)) |
858 | 859 | position.phrase_types.add(phrase) |
... | ... | @@ -880,6 +881,7 @@ class InfP: |
880 | 881 | phraseologic=False, |
881 | 882 | defaults={'priority': 0}) |
882 | 883 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
884 | + attributes=get_attributes(main_type.name, self), | |
883 | 885 | lexicalized_phrase=None, |
884 | 886 | text_rep=str(self)) |
885 | 887 | position.phrase_types.add(phrase) |
... | ... | @@ -921,9 +923,11 @@ class LexInfP: |
921 | 923 | phraseologic=False, |
922 | 924 | defaults={'priority': 0}) |
923 | 925 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
926 | + attributes=get_attributes(lex_type.name, self._infp), | |
924 | 927 | lexicalized_phrase=None, |
925 | 928 | text_rep=str(self._infp)) |
926 | 929 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
930 | + attributes=get_lex_attributes(lex_type.name, self), | |
927 | 931 | lexicalized_phrase=lex, |
928 | 932 | text_rep=str(self)) |
929 | 933 | position.phrase_types.add(phrase) |
... | ... | @@ -960,6 +964,7 @@ class PrepGerP: |
960 | 964 | phraseologic=True, |
961 | 965 | defaults={'priority': 0}) |
962 | 966 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
967 | + attributes=get_attributes(main_type.name, self), | |
963 | 968 | lexicalized_phrase=None, |
964 | 969 | text_rep=str(self)) |
965 | 970 | position.phrase_types.add(phrase) |
... | ... | @@ -1002,9 +1007,11 @@ class LexPrepGerP: |
1002 | 1007 | phraseologic=False, |
1003 | 1008 | defaults={'priority': 0}) |
1004 | 1009 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
1010 | + attributes=get_attributes(lex_type.name, self._prepgerp), | |
1005 | 1011 | lexicalized_phrase=None, |
1006 | 1012 | text_rep=str(self._prepgerp)) |
1007 | 1013 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1014 | + attributes=get_lex_attributes(lex_type.name, self), | |
1008 | 1015 | lexicalized_phrase=lex, |
1009 | 1016 | text_rep=str(self)) |
1010 | 1017 | position.phrase_types.add(phrase) |
... | ... | @@ -1040,6 +1047,7 @@ class PPasP: |
1040 | 1047 | phraseologic=True, |
1041 | 1048 | defaults={'priority': 0}) |
1042 | 1049 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1050 | + attributes=get_attributes(main_type.name, self), | |
1043 | 1051 | lexicalized_phrase=None, |
1044 | 1052 | text_rep=str(self)) |
1045 | 1053 | position.phrase_types.add(phrase) |
... | ... | @@ -1079,9 +1087,11 @@ class LexPPasP: |
1079 | 1087 | phraseologic=False, |
1080 | 1088 | defaults={'priority': 0}) |
1081 | 1089 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
1090 | + attributes=get_attributes(lex_type.name, self._ppasp), | |
1082 | 1091 | lexicalized_phrase=None, |
1083 | 1092 | text_rep=str(self._ppasp)) |
1084 | 1093 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1094 | + attributes=get_lex_attributes(lex_type.name, self), | |
1085 | 1095 | lexicalized_phrase=lex, |
1086 | 1096 | text_rep=str(self)) |
1087 | 1097 | position.phrase_types.add(phrase) |
... | ... | @@ -1119,6 +1129,7 @@ class PrepPPasP: |
1119 | 1129 | phraseologic=False, |
1120 | 1130 | defaults={'priority': 0}) |
1121 | 1131 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1132 | + attributes=get_attributes(main_type.name, self), | |
1122 | 1133 | lexicalized_phrase=None, |
1123 | 1134 | text_rep=str(self)) |
1124 | 1135 | position.phrase_types.add(phrase) |
... | ... | @@ -1158,9 +1169,11 @@ class LexPrepPPasP: |
1158 | 1169 | phraseologic=False, |
1159 | 1170 | defaults={'priority': 0}) |
1160 | 1171 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
1172 | + attributes=get_attributes(lex_type.name, self._prepppasp), | |
1161 | 1173 | lexicalized_phrase=None, |
1162 | 1174 | text_rep=str(self._prepppasp)) |
1163 | 1175 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1176 | + attributes=get_lex_attributes(lex_type.name, self), | |
1164 | 1177 | lexicalized_phrase=lex, |
1165 | 1178 | text_rep=str(self)) |
1166 | 1179 | position.phrase_types.add(phrase) |
... | ... | @@ -1280,6 +1293,7 @@ class XP: |
1280 | 1293 | phraseologic=False, |
1281 | 1294 | defaults={'priority': 0}) |
1282 | 1295 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1296 | + attributes=get_attributes(main_type.name, self), | |
1283 | 1297 | lexicalized_phrase=None, |
1284 | 1298 | text_rep=str(self)) |
1285 | 1299 | position.phrase_types.add(phrase) |
... | ... | @@ -1319,6 +1333,9 @@ class LexXP: |
1319 | 1333 | return self._id |
1320 | 1334 | |
1321 | 1335 | def store(self, position): |
1336 | + print(self) | |
1337 | + print(self._xp) | |
1338 | + print(self._lex) | |
1322 | 1339 | main_type, _ = PhraseTypeModel.objects.get_or_create(name='lex', |
1323 | 1340 | phraseologic=True, |
1324 | 1341 | defaults={'priority': 0}) |
... | ... | @@ -1326,9 +1343,11 @@ class LexXP: |
1326 | 1343 | phraseologic=False, |
1327 | 1344 | defaults={'priority': 0}) |
1328 | 1345 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
1346 | + attributes=get_attributes(lex_type.name, self._xp), | |
1329 | 1347 | lexicalized_phrase=None, |
1330 | 1348 | text_rep=str(self._xp)) |
1331 | 1349 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1350 | + attributes=get_lex_attributes(lex_type.name, self), | |
1332 | 1351 | lexicalized_phrase=lex, |
1333 | 1352 | text_rep=str(self)) |
1334 | 1353 | position.phrase_types.add(phrase) |
... | ... | @@ -1362,6 +1381,7 @@ class AdvP: |
1362 | 1381 | phraseologic=False, |
1363 | 1382 | defaults={'priority': 0}) |
1364 | 1383 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1384 | + attributes=get_attributes(main_type.name, self), | |
1365 | 1385 | lexicalized_phrase=None, |
1366 | 1386 | text_rep=str(self)) |
1367 | 1387 | position.phrase_types.add(phrase) |
... | ... | @@ -1397,9 +1417,11 @@ class LexAdvP: |
1397 | 1417 | phraseologic=False, |
1398 | 1418 | defaults={'priority': 0}) |
1399 | 1419 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
1420 | + attributes=get_attributes(lex_type.name, self._advp), | |
1400 | 1421 | lexicalized_phrase=None, |
1401 | 1422 | text_rep=str(self._advp)) |
1402 | 1423 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1424 | + attributes=get_lex_attributes(lex_type.name, self), | |
1403 | 1425 | lexicalized_phrase=lex, |
1404 | 1426 | text_rep=str(self)) |
1405 | 1427 | position.phrase_types.add(phrase) |
... | ... | @@ -1435,6 +1457,7 @@ class AdjP: |
1435 | 1457 | phraseologic=False, |
1436 | 1458 | defaults={'priority': 0}) |
1437 | 1459 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1460 | + attributes=get_attributes(main_type.name, self), | |
1438 | 1461 | lexicalized_phrase=None, |
1439 | 1462 | text_rep=str(self)) |
1440 | 1463 | position.phrase_types.add(phrase) |
... | ... | @@ -1474,9 +1497,11 @@ class LexAdjP: |
1474 | 1497 | phraseologic=False, |
1475 | 1498 | defaults={'priority': 0}) |
1476 | 1499 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
1500 | + attributes=get_attributes(lex_type.name, self._adjp), | |
1477 | 1501 | lexicalized_phrase=None, |
1478 | 1502 | text_rep=str(self._adjp)) |
1479 | 1503 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1504 | + attributes=get_lex_attributes(lex_type.name, self), | |
1480 | 1505 | lexicalized_phrase=lex, |
1481 | 1506 | text_rep=str(self)) |
1482 | 1507 | position.phrase_types.add(phrase) |
... | ... | @@ -1513,6 +1538,7 @@ class Compar: |
1513 | 1538 | phraseologic=False, |
1514 | 1539 | defaults={'priority': 0}) |
1515 | 1540 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1541 | + attributes=get_attributes(main_type.name, self), | |
1516 | 1542 | lexicalized_phrase=None, |
1517 | 1543 | text_rep=str(self)) |
1518 | 1544 | position.phrase_types.add(phrase) |
... | ... | @@ -1544,9 +1570,11 @@ class LexCompar: |
1544 | 1570 | phraseologic=False, |
1545 | 1571 | defaults={'priority': 0}) |
1546 | 1572 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
1573 | + attributes=get_attributes(lex_type.name, self._compar), | |
1547 | 1574 | lexicalized_phrase=None, |
1548 | 1575 | text_rep=str(self._compar)) |
1549 | 1576 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1577 | + attributes=get_lex_attributes(lex_type.name, self), | |
1550 | 1578 | lexicalized_phrase=lex, |
1551 | 1579 | text_rep=str(self)) |
1552 | 1580 | position.phrase_types.add(phrase) |
... | ... | @@ -1581,6 +1609,7 @@ class Qub: |
1581 | 1609 | phraseologic=False, |
1582 | 1610 | defaults={'priority': 0}) |
1583 | 1611 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1612 | + attributes=empty_attributes(), | |
1584 | 1613 | lexicalized_phrase=None, |
1585 | 1614 | text_rep=str(self)) |
1586 | 1615 | position.phrase_types.add(phrase) |
... | ... | @@ -1614,9 +1643,11 @@ class LexQub: |
1614 | 1643 | phraseologic=False, |
1615 | 1644 | defaults={'priority': 0}) |
1616 | 1645 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
1646 | + attributes=empty_attributes(), | |
1617 | 1647 | lexicalized_phrase=None, |
1618 | 1648 | text_rep=str(self._qub)) |
1619 | 1649 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1650 | + attributes=empty_attributes(), | |
1620 | 1651 | lexicalized_phrase=lex, |
1621 | 1652 | text_rep=str(self)) |
1622 | 1653 | position.phrase_types.add(phrase) |
... | ... | @@ -1650,6 +1681,7 @@ class Refl: |
1650 | 1681 | phraseologic=False, |
1651 | 1682 | defaults={'priority': 0}) |
1652 | 1683 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1684 | + attributes=empty_attributes(), | |
1653 | 1685 | lexicalized_phrase=None, |
1654 | 1686 | text_rep=str(self)) |
1655 | 1687 | position.phrase_types.add(phrase) |
... | ... | @@ -1675,6 +1707,7 @@ class Recip: |
1675 | 1707 | phraseologic=False, |
1676 | 1708 | defaults={'priority': 0}) |
1677 | 1709 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1710 | + attributes=empty_attributes(), | |
1678 | 1711 | lexicalized_phrase=None, |
1679 | 1712 | text_rep=str(self)) |
1680 | 1713 | position.phrase_types.add(phrase) |
... | ... | @@ -1700,6 +1733,7 @@ class OR: |
1700 | 1733 | phraseologic=False, |
1701 | 1734 | defaults={'priority': 0}) |
1702 | 1735 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1736 | + attributes=empty_attributes(), | |
1703 | 1737 | lexicalized_phrase=None, |
1704 | 1738 | text_rep=str(self)) |
1705 | 1739 | position.phrase_types.add(phrase) |
... | ... | @@ -1726,6 +1760,7 @@ class E: |
1726 | 1760 | phraseologic=False, |
1727 | 1761 | defaults={'priority': 0}) |
1728 | 1762 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1763 | + attributes=empty_attributes(), | |
1729 | 1764 | lexicalized_phrase=None, |
1730 | 1765 | text_rep=str(self)) |
1731 | 1766 | position.phrase_types.add(phrase) |
... | ... | @@ -1751,6 +1786,7 @@ class PossP: |
1751 | 1786 | phraseologic=False, |
1752 | 1787 | defaults={'priority': 0}) |
1753 | 1788 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1789 | + attributes=empty_attributes(), | |
1754 | 1790 | lexicalized_phrase=None, |
1755 | 1791 | text_rep=str(self)) |
1756 | 1792 | position.phrase_types.add(phrase) |
... | ... | @@ -1776,6 +1812,7 @@ class DistrP: |
1776 | 1812 | phraseologic=False, |
1777 | 1813 | defaults={'priority': 0}) |
1778 | 1814 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1815 | + attributes=empty_attributes(), | |
1779 | 1816 | lexicalized_phrase=None, |
1780 | 1817 | text_rep=str(self)) |
1781 | 1818 | position.phrase_types.add(phrase) |
... | ... | @@ -1811,9 +1848,11 @@ class Fixed: |
1811 | 1848 | phraseologic=False, |
1812 | 1849 | defaults={'priority': 0}) |
1813 | 1850 | lex, _ = PhraseType.objects.get_or_create(main_type=lex_type, |
1851 | + attributes=get_attributes(lex_type.name, self._phrase), | |
1814 | 1852 | lexicalized_phrase=None, |
1815 | 1853 | text_rep=str(self._phrase)) |
1816 | 1854 | phrase, _ = PhraseType.objects.get_or_create(main_type=main_type, |
1855 | + attributes=get_attributes('fixed', self), | |
1817 | 1856 | lexicalized_phrase=lex, |
1818 | 1857 | text_rep=str(self)) |
1819 | 1858 | position.phrase_types.add(phrase) |
... | ... |
importer/PhraseAttributes.py
0 → 100644
1 | +#! /usr/bin/python | |
2 | +# -*- coding: utf-8 -*- | |
3 | + | |
4 | +from syntax.models_phrase import * | |
5 | +from syntax.models import Position | |
6 | + | |
7 | +def get_case(name): | |
8 | + return Case.objects.get(name=name) | |
9 | + | |
10 | +def get_prep(name): | |
11 | + prep, _ = Preposition.objects.get_or_create(name=name) | |
12 | + return prep | |
13 | + | |
14 | +def get_comprep(name): | |
15 | + prep, _ = ComplexPreposition.objects.get_or_create(name=name) | |
16 | + return prep | |
17 | + | |
18 | +def get_cptype(name): | |
19 | + cptype, _ = CPType.objects.get_or_create(name=name) | |
20 | + return cptype | |
21 | + | |
22 | +def get_cpreals(names): | |
23 | + cpreals = [] | |
24 | + if names: | |
25 | + for name in names: | |
26 | + cpreal, _ = CPRealisation.objects.get_or_create(name=name) | |
27 | + cpreals.append(cpreal) | |
28 | + return cpreals | |
29 | + | |
30 | +def get_aspect(name): | |
31 | + return PhraseAspect.objects.get(name=name) | |
32 | + | |
33 | +def get_neg(name): | |
34 | + return PhraseNegativity.objects.get(name=name) | |
35 | + | |
36 | +def get_inhsie(name): | |
37 | + return PhraseInherentSie.objects.get(name=name) | |
38 | + | |
39 | +def get_advcat(name): | |
40 | + return AdverbialCategory.objects.get(name=name) | |
41 | + | |
42 | +def get_comparcat(name): | |
43 | + comparcat, _ = ComparCategory.objects.get_or_create(name=name) | |
44 | + return comparcat | |
45 | + | |
46 | +def get_num(name): | |
47 | + return Number.objects.get(name=name) | |
48 | + | |
49 | +def get_gend(name): | |
50 | + return Gender.objects.get(name=name) | |
51 | + | |
52 | +def get_deg(name): | |
53 | + return Degree.objects.get(name=name) | |
54 | + | |
55 | +def store_phrase(phrase): | |
56 | + dummy_postition = Position.objects.get(id=1) | |
57 | + phrase.store(dummy_postition) | |
58 | + # TODO? a better solution: make store() return the stored object? | |
59 | + # this is ugly: retrieving the just-stored phrase by its text_rep... | |
60 | + return PhraseType.objects.get(text_rep=str(phrase)) | |
61 | + | |
62 | +# for compar | |
63 | +def get_lexes(lexes): | |
64 | + return [store_phrase(lex) for lex in lexes] | |
65 | + | |
66 | +# for lex(xp), lex(advp) | |
67 | +def get_lex(phrase): | |
68 | + return store_phrase(phrase) | |
69 | + | |
70 | +# for fixed phrases | |
71 | +def get_phrase(phrase): | |
72 | + return store_phrase(phrase) | |
73 | + | |
74 | +def get_text(text): | |
75 | + return text | |
76 | + | |
77 | +#------------------------------------------------------------------------------- | |
78 | + | |
79 | +''' | |
80 | +transformations = { | |
81 | + 'case' : get_case, | |
82 | + 'prep' : get_prep, | |
83 | + 'comprep' : get_comprep, | |
84 | + 'cptype' : get_cptype, | |
85 | + 'aspect' : get_aspect, | |
86 | + 'neg' : get_neg, | |
87 | + 'advcat' : get_advcat, | |
88 | + 'comparcat' : get_comparcat, | |
89 | + # lex | |
90 | + 'num' : get_num, | |
91 | +} | |
92 | +''' | |
93 | + | |
94 | +class AttributesHelper(object): | |
95 | + | |
96 | + # many-to-many fields can’t be initialised on model object creation | |
97 | + def __init__(self, attrs_cls=None, lex_attrs_cls=None, attr_getters=None, m2m_attr_getters={}, lex_attr_getters=None, lex_m2m_attr_getters={}): | |
98 | + self.attrs_cls = attrs_cls | |
99 | + self.lex_attrs_cls = lex_attrs_cls | |
100 | + self.attr_getters = attr_getters | |
101 | + self.m2m_attr_getters = m2m_attr_getters | |
102 | + self.lex_attr_getters = lex_attr_getters | |
103 | + self.lex_m2m_attr_getters = lex_m2m_attr_getters | |
104 | + | |
105 | +prepp_attr_getters = { | |
106 | + 'prep' : lambda x: x._prep._value, | |
107 | + 'case' : lambda x: x._prep._case._value, | |
108 | +} | |
109 | + | |
110 | +attrs_helpers = { | |
111 | + | |
112 | + 'np' : AttributesHelper( | |
113 | + attrs_cls=NPAttributes, | |
114 | + lex_attrs_cls=LexNPAttributes, | |
115 | + attr_getters={ | |
116 | + 'case' : lambda x: x._case._value, | |
117 | + }, | |
118 | + lex_attr_getters={ | |
119 | + 'num' : lambda x: x._number, | |
120 | + }), | |
121 | + 'nump' : AttributesHelper( | |
122 | + attrs_cls=NumPAttributes, | |
123 | + attr_getters={ | |
124 | + 'case' : lambda x: x._case._value, | |
125 | + }, | |
126 | + #lex_attr_getters={ | |
127 | + # 'num' : lambda x: x._number, | |
128 | + #} | |
129 | + ), | |
130 | + 'adjp' : AttributesHelper( | |
131 | + attrs_cls=AdjPAttributes, | |
132 | + lex_attrs_cls=LexAdjPAttributes, | |
133 | + attr_getters={ | |
134 | + 'case' : lambda x: x._case._value, | |
135 | + }, | |
136 | + lex_attr_getters={ | |
137 | + 'num' : lambda x: x._number, | |
138 | + 'gend' : lambda x: x._gender, | |
139 | + 'deg' : lambda x: x._degree, | |
140 | + }), | |
141 | + 'ppasp' : AttributesHelper( | |
142 | + attrs_cls=PPasPAttributes, | |
143 | + lex_attrs_cls=LexPPasPAttributes, | |
144 | + attr_getters={ | |
145 | + 'case' : lambda x: x._case._value, | |
146 | + }, | |
147 | + lex_attr_getters={ | |
148 | + 'num' : lambda x: x._number, | |
149 | + 'gend' : lambda x: x._gender, | |
150 | + 'neg' : lambda x: x._negativity, | |
151 | + }), | |
152 | + 'prepnp' : AttributesHelper( | |
153 | + attrs_cls=PrepNPAttributes, | |
154 | + lex_attrs_cls=LexPrepNPAttributes, | |
155 | + attr_getters=prepp_attr_getters, | |
156 | + lex_attr_getters={ | |
157 | + 'num' : lambda x: x._number, | |
158 | + }), | |
159 | + 'prepnump' : AttributesHelper( | |
160 | + attrs_cls=PrepNumPAttributes, | |
161 | + #lex_attrs_cls=LexPrepNumPAttributes, | |
162 | + attr_getters=prepp_attr_getters, | |
163 | + #lex_attr_getters={ | |
164 | + # 'num' : lambda x: x._number, | |
165 | + #} | |
166 | + ), | |
167 | + 'prepadjp' : AttributesHelper( | |
168 | + attrs_cls=PrepAdjPAttributes, | |
169 | + lex_attrs_cls=LexPrepAdjPAttributes, | |
170 | + attr_getters=prepp_attr_getters, | |
171 | + lex_attr_getters={ | |
172 | + 'num' : lambda x: x._number, | |
173 | + 'gend' : lambda x: x._gender, | |
174 | + 'deg' : lambda x: x._degree, | |
175 | + }), | |
176 | + 'prepppasp' : AttributesHelper( | |
177 | + attrs_cls=PrepPPasPAttributes, | |
178 | + lex_attrs_cls=LexPrepPPasPAttributes, | |
179 | + attr_getters=prepp_attr_getters, | |
180 | + lex_attr_getters={ | |
181 | + 'num' : lambda x: x._number, | |
182 | + 'gend' : lambda x: x._gender, | |
183 | + 'neg' : lambda x: x._negativity, | |
184 | + }), | |
185 | + 'prepgerp' : AttributesHelper( | |
186 | + attrs_cls=PrepGerPAttributes, | |
187 | + lex_attrs_cls=LexPrepGerPAttributes, | |
188 | + attr_getters=prepp_attr_getters, | |
189 | + lex_attr_getters={ | |
190 | + 'num' : lambda x: x._number, | |
191 | + 'neg' : lambda x: x._negativity, | |
192 | + 'inhsie' : lambda x: x._inherent_sie, | |
193 | + }), | |
194 | + 'comprepnp' : AttributesHelper( | |
195 | + attrs_cls=ComPrepNPAttributes, | |
196 | + attr_getters={ | |
197 | + 'comprep' : lambda x: x._prep._value, | |
198 | + }), | |
199 | + 'cp' : AttributesHelper( | |
200 | + attrs_cls=CPAttributes, | |
201 | + lex_attrs_cls=LexCPAttributes, | |
202 | + attr_getters={ | |
203 | + 'cptype' : lambda x: x._type._value, | |
204 | + }, | |
205 | + lex_attr_getters={ | |
206 | + 'neg' : lambda x: x._negativity, | |
207 | + 'inhsie' : lambda x: x._inherent_sie, | |
208 | + }, | |
209 | + m2m_attr_getters={ | |
210 | + 'cpreals' : lambda x: x._type._realisations, | |
211 | + }), | |
212 | + 'ncp' : AttributesHelper( | |
213 | + attrs_cls=NCPAttributes, | |
214 | + lex_attrs_cls=LexNCPAttributes, | |
215 | + attr_getters={ | |
216 | + 'case' : lambda x: x._case._value, | |
217 | + 'cptype' : lambda x: x._type._value, | |
218 | + }, | |
219 | + lex_attr_getters={ | |
220 | + 'neg' : lambda x: x._negativity, | |
221 | + 'inhsie' : lambda x: x._inherent_sie, | |
222 | + }, | |
223 | + m2m_attr_getters={ | |
224 | + 'cpreals' : lambda x: x._type._realisations, | |
225 | + }), | |
226 | + 'prepncp' : AttributesHelper( | |
227 | + attrs_cls=PrepNCPAttributes, | |
228 | + attr_getters={ | |
229 | + 'prep' : lambda x: x._prep._value, | |
230 | + 'case' : lambda x: x._prep._case._value, | |
231 | + 'cptype' : lambda x: x._type._value, | |
232 | + }, | |
233 | + m2m_attr_getters={ | |
234 | + 'cpreals' : lambda x: x._type._realisations, | |
235 | + }), | |
236 | + 'infp' : AttributesHelper( | |
237 | + attrs_cls=InfPAttributes, | |
238 | + lex_attrs_cls=LexInfPAttributes, | |
239 | + attr_getters={ | |
240 | + 'aspect' : lambda x: x._aspect._value, | |
241 | + }, | |
242 | + lex_attr_getters={ | |
243 | + 'neg' : lambda x: x._negativity, | |
244 | + 'inhsie' : lambda x: x._inherent_sie, | |
245 | + }), | |
246 | + 'xp' : AttributesHelper( | |
247 | + attrs_cls=XPAttributes, | |
248 | + lex_attrs_cls=LexXPAttributes, | |
249 | + attr_getters={ | |
250 | + 'advcat' : lambda x: x._category._value, | |
251 | + }, | |
252 | + lex_attr_getters={ | |
253 | + 'lex' : lambda x: x._lex, | |
254 | + }), | |
255 | + 'advp' : AttributesHelper( | |
256 | + attrs_cls=AdvPAttributes, | |
257 | + lex_attrs_cls=LexAdvPAttributes, | |
258 | + attr_getters={ | |
259 | + 'advcat' : lambda x: x._category._value, | |
260 | + }, | |
261 | + lex_attr_getters={ | |
262 | + 'deg' : lambda x: x._degree, | |
263 | + }), | |
264 | + 'compar' : AttributesHelper( | |
265 | + attrs_cls=ComparAttributes, | |
266 | + lex_attrs_cls=LexComparAttributes, | |
267 | + attr_getters={ | |
268 | + 'comparcat' : lambda x: x._category._value, | |
269 | + }, | |
270 | + lex_attr_getters={}, | |
271 | + lex_m2m_attr_getters={ | |
272 | + 'lexes' : lambda x: x._lexes, | |
273 | + }), | |
274 | + 'fixed' : AttributesHelper( | |
275 | + attrs_cls=FixedAttributes, | |
276 | + attr_getters={ | |
277 | + 'phrase' : lambda x: x._phrase, | |
278 | + 'text' : lambda x: x._text, | |
279 | + }), | |
280 | +} | |
281 | + | |
282 | +''' | |
283 | +attrs_cls = { | |
284 | + 'np' : NPAttributes, | |
285 | + 'adjp' : NPAttributes, | |
286 | + 'prepnp' : PrepNPAttributes, | |
287 | + 'prepadjp' : PrepNPAttributes, | |
288 | + 'comprepnp' : ComPrepNPAttributes, | |
289 | + 'cp' : CPAttributes, | |
290 | + 'ncp' : NCPAttributes, | |
291 | + 'prepncp' : PrepNCPAttributes, | |
292 | + 'infp' : InfPAttributes, | |
293 | + 'xp' : XPAttributes, | |
294 | + 'advp' : XPAttributes, | |
295 | + 'compar' : ComparAttributes, | |
296 | +} | |
297 | +''' | |
298 | + | |
299 | +#------------------------------------------------------------------------------- | |
300 | + | |
301 | +def empty_attributes(): | |
302 | + attr, _ = EmptyAttributes.objects.get_or_create(empty='_') | |
303 | + return attr | |
304 | + | |
305 | +def do_get_attributes(phrase_type, phrase_obj, lex): | |
306 | + helper = attrs_helpers[phrase_type] | |
307 | + cls = helper.lex_attrs_cls if lex else helper.attrs_cls | |
308 | + attr_getters = helper.lex_attr_getters if lex else helper.attr_getters | |
309 | + m2m_attr_getters = helper.lex_m2m_attr_getters if lex else helper.m2m_attr_getters | |
310 | + kwargs = { key : eval('get_{}'.format(key))(getter(phrase_obj)) for key, getter in attr_getters.items() } | |
311 | + m2m_attrs = { key : eval('get_{}'.format(key))(getter(phrase_obj)) for key, getter in m2m_attr_getters.items() } | |
312 | + if cls is not None: | |
313 | + attrs, _ = cls.objects.get_or_create(**kwargs) | |
314 | + for key, values in m2m_attrs.items(): | |
315 | + if values: | |
316 | + getattr(attrs, key).set(values) | |
317 | + print(attrs) | |
318 | + return attrs | |
319 | + return None | |
320 | + | |
321 | +def get_attributes(phrase_type, phrase_obj, **kwargs): | |
322 | + return do_get_attributes(phrase_type, phrase_obj, False, **kwargs) | |
323 | + | |
324 | +def get_lex_attributes(phrase_type, phrase_obj, **kwargs): | |
325 | + return do_get_attributes(phrase_type, phrase_obj, True, **kwargs) | |
... | ... |
importer/Position.py
... | ... | @@ -103,6 +103,7 @@ class Position: |
103 | 103 | phrases_count=len(self._phrases)) |
104 | 104 | self._db_id = position.id |
105 | 105 | for phrase in self._phrases: |
106 | + print('==============', phrase) | |
106 | 107 | phrase.store(position) |
107 | 108 | phrase_text = str(phrase) |
108 | 109 | desc_count = NaturalLanguageDescription.objects.filter( |
... | ... |
importer/Realizations.py
1 | 1 | #! /usr/bin/python |
2 | 2 | # -*- coding: utf-8 -*- |
3 | 3 | |
4 | -from syntax.models import Schema, Position, PhraseType | |
4 | +from syntax.models import Schema, Position | |
5 | +from syntax.models_phrase import PhraseType | |
5 | 6 | from semantics.models import Frame |
6 | 7 | from connections.models import ArgumentConnection, SchemaHook |
7 | 8 | |
... | ... |
shellvalier/urls.py
... | ... | @@ -16,7 +16,7 @@ Including another URLconf |
16 | 16 | from django.contrib import admin |
17 | 17 | from django.urls import include, path |
18 | 18 | |
19 | -from common.views import dash | |
19 | +from common.views import dash, error_400, error_404 | |
20 | 20 | |
21 | 21 | urlpatterns = [ |
22 | 22 | path('entries/', include('entries.urls')), |
... | ... | @@ -26,3 +26,6 @@ urlpatterns = [ |
26 | 26 | path('admin/', admin.site.urls, name='admin'), |
27 | 27 | path('', dash, name='dash'), |
28 | 28 | ] |
29 | + | |
30 | +handler400 = error_400 | |
31 | +handler404 = error_404 | |
... | ... |
syntax/admin.py
1 | 1 | from django.contrib import admin |
2 | 2 | |
3 | -from .models import Aspect, Control, InherentSie, Negativity, PhraseType, PhraseTypeModel, Position, \ | |
3 | +from .models import Aspect, Control, InherentSie, Negativity, Position, \ | |
4 | 4 | PredicativeControl, Predicativity, Schema, SchemaOpinion, Subentry, SyntacticFunction |
5 | 5 | |
6 | +from .models_phrase import PhraseType, PhraseTypeModel | |
7 | + | |
6 | 8 | admin.site.register(Aspect) |
7 | 9 | admin.site.register(Control) |
8 | 10 | admin.site.register(InherentSie) |
... | ... |
syntax/management/commands/import_tei.py
... | ... | @@ -10,7 +10,11 @@ from importer.WalentyPreprocessXML import WalentyPreprocessTeiHandler |
10 | 10 | from shellvalier.settings import BASE_DIR |
11 | 11 | from connections.models import POS, Status |
12 | 12 | from examples.models import ExampleOpinion, ExampleSource |
13 | -from syntax.models import SchemaOpinion, Aspect, InherentSie, Negativity, Predicativity, SyntacticFunction, Control, PredicativeControl | |
13 | +from syntax.models import SchemaOpinion, Aspect, InherentSie, Negativity, Predicativity, SyntacticFunction, Control, PredicativeControl, Position | |
14 | +from syntax.models_phrase import ( | |
15 | + Case, PhraseAspect, AdverbialCategory, PhraseNegativity, PhraseInherentSie, | |
16 | + Number, Gender, Degree, | |
17 | +) | |
14 | 18 | from semantics.models import FrameOpinion, ArgumentRole, SemanticRole, RoleAttribute, PredefinedSelectionalPreference, SelectionalPreferenceRelation |
15 | 19 | |
16 | 20 | |
... | ... | @@ -57,6 +61,7 @@ def import_constants(): |
57 | 61 | import_preference_relations() |
58 | 62 | import_examples_sources() |
59 | 63 | import_examples_opinions() |
64 | + import_phrase_attributes() | |
60 | 65 | pass |
61 | 66 | |
62 | 67 | def import_poses(): |
... | ... | @@ -152,6 +157,7 @@ def import_preference_relations(): |
152 | 157 | relat = SelectionalPreferenceRelation(plwn_id=id, key=name) |
153 | 158 | relat.save() |
154 | 159 | |
160 | + | |
155 | 161 | def import_examples_sources(): |
156 | 162 | sources = [(0, u'NKJP0.5M'), (1, u'NKJP1.2M'), (2, u'NKJP30M'), (3, u'NKJP250M'), (4, u'NKJP300M'), (5, u'NKJP500M'), (6, u'NKJP1800M'), (7, u'linguistic_literature'), (8, u'other_literature'), (9, u'own')] |
157 | 163 | for pri, name in sources: |
... | ... | @@ -164,3 +170,65 @@ def import_examples_opinions(): |
164 | 170 | eo = ExampleOpinion(key=name, priority=pri) |
165 | 171 | eo.save() |
166 | 172 | |
173 | +def import_phrase_attributes(): | |
174 | + import_cases() | |
175 | + import_phrase_aspects() | |
176 | + import_phrase_negativities() | |
177 | + import_phrase_inherent_sies() | |
178 | + import_adverbial_categories() | |
179 | + import_numbers() | |
180 | + import_genders() | |
181 | + import_degrees() | |
182 | + # TODO this is quite terrible... create a dummy position for storing phrases inside a lex | |
183 | + # the store() method for phrases requires a position | |
184 | + dummy_position = Position() | |
185 | + dummy_position.save() | |
186 | + assert (dummy_position.id == 1) | |
187 | + | |
188 | +def import_cases(): | |
189 | + cases = [(0, u'str'), (1, u'nom'), (2, u'gen'), (3, u'dat'), (4, u'acc'), (5, u'inst'), (6, u'loc'), (10, u'pred'), (11, u'part'), (12, u'postp'), (13, u'agr')] | |
190 | + for pri, name in cases: | |
191 | + case = Case(name=name, priority=pri) | |
192 | + case.save() | |
193 | + | |
194 | +def import_phrase_aspects(): | |
195 | + aspects = [(10, u'imperf'), (20, u'perf'), (30, u'_')] | |
196 | + for pri, name in aspects: | |
197 | + aspect = PhraseAspect(name=name, priority=pri) | |
198 | + aspect.save() | |
199 | + | |
200 | +def import_phrase_negativities(): | |
201 | + negativities = [(10, u'aff'), (20, u'neg'), (30, u'_')] | |
202 | + for pri, name in negativities: | |
203 | + negativity = PhraseNegativity(name=name, priority=pri) | |
204 | + negativity.save() | |
205 | + | |
206 | +def import_phrase_inherent_sies(): | |
207 | + sies = [(10, u'się'), (20, u'')] | |
208 | + for pri, name in sies: | |
209 | + sie = PhraseInherentSie(name=name, priority=pri) | |
210 | + sie.save() | |
211 | + | |
212 | +def import_adverbial_categories(): | |
213 | + advcats = [(1, u'locat'), (2, u'abl'), (3, u'adl'), (4, u'perl'), (5, u'temp'), (6, u'dur'), (7, 'mod'), (8, 'caus'), (9, 'dest'), (10, 'instr'), (11, 'pron'), (12, 'misc')] | |
214 | + for pri, name in advcats: | |
215 | + advcat = AdverbialCategory(name=name, priority=pri) | |
216 | + advcat.save() | |
217 | + | |
218 | +def import_numbers(): | |
219 | + numbers = [(1, u'sg'), (2, u'pl'), (10, u'agr'), (20, u'_')] | |
220 | + for pri, name in numbers: | |
221 | + number = Number(name=name, priority=pri) | |
222 | + number.save() | |
223 | + | |
224 | +def import_genders(): | |
225 | + genders = [(1, u'm1'), (2, u'm2'), (3, u'm3'), (4, u'f'), (5, u'n'), (10, u'agr')] | |
226 | + for pri, name in genders: | |
227 | + gender = Gender(name=name, priority=pri) | |
228 | + gender.save() | |
229 | + | |
230 | +def import_degrees(): | |
231 | + degrees = [(1, u'pos'), (2, u'com'), (3, u'sup'), (20, u'_')] | |
232 | + for pri, name in degrees: | |
233 | + degree = Degree(name=name, priority=pri) | |
234 | + degree.save() | |
... | ... |
syntax/models.py
1 | 1 | from django.db import models |
2 | 2 | |
3 | - | |
4 | 3 | class Subentry(models.Model): |
5 | 4 | entry = models.ForeignKey('connections.Entry', related_name='subentries', on_delete=models.PROTECT) |
6 | 5 | aspect = models.ForeignKey('Aspect', related_name='subentries', null=True, |
... | ... | @@ -146,10 +145,11 @@ class PredicativeControl(models.Model): |
146 | 145 | def __str__(self): |
147 | 146 | return self.name |
148 | 147 | |
149 | - | |
148 | +''' | |
150 | 149 | class PhraseType(models.Model): |
151 | 150 | # osobne wpisy dla występujących ograniczonych realizacji, np. xp(locat[prepnp(u,gen),prepnp(w,loc)] |
152 | 151 | main_type = models.ForeignKey('PhraseTypeModel', related_name='main_phrase_types', on_delete=models.PROTECT) |
152 | + attributes = models.ForeignKey(PhraseTypeAttributes, on_delete=models.PROTECT) | |
153 | 153 | lexicalized_phrase = models.ForeignKey('PhraseType', related_name='lex_phrases', null=True, |
154 | 154 | on_delete=models.PROTECT) |
155 | 155 | text_rep = models.TextField(unique=True) |
... | ... | @@ -167,10 +167,13 @@ class PhraseTypeModel(models.Model): |
167 | 167 | priority = models.PositiveIntegerField() |
168 | 168 | |
169 | 169 | class Meta: |
170 | - ordering = ['priority'] | |
170 | + #ordering = ['priority'] | |
171 | + # TODO define an ordering priority for this model | |
172 | + ordering = ['name'] | |
171 | 173 | |
172 | 174 | def __str__(self): |
173 | 175 | return self.name |
176 | +''' | |
174 | 177 | |
175 | 178 | class NaturalLanguageDescription(models.Model): |
176 | 179 | negativity = models.ForeignKey(Negativity, null=True, on_delete=models.PROTECT) |
... | ... | @@ -185,4 +188,3 @@ class NaturalLanguageDescription(models.Model): |
185 | 188 | |
186 | 189 | def __str__(self): |
187 | 190 | return self.description |
188 | - | |
... | ... |
syntax/models_phrase.py
0 → 100644
1 | +from django.db import models | |
2 | + | |
3 | + | |
4 | +# === Atomic phrase attributes ================================================= | |
5 | + | |
6 | +# TODO a lot od repeated code, but can’t use simple inheritance: | |
7 | +# name values repeat across attributes, e.g. prep ‘jako’ / compar cat. ‘jako’ | |
8 | + | |
9 | +# --- Priority-sorted ---------------------------------------------------------- | |
10 | + | |
11 | +class Case(models.Model): | |
12 | + name = models.CharField(max_length=16, unique=True) | |
13 | + priority = models.PositiveIntegerField() | |
14 | + class Meta: | |
15 | + ordering = ['priority'] | |
16 | + def __str__(self): | |
17 | + return self.name | |
18 | + | |
19 | +class PhraseAspect(models.Model): | |
20 | + name = models.CharField(max_length=16, unique=True) | |
21 | + priority = models.PositiveIntegerField() | |
22 | + class Meta: | |
23 | + ordering = ['priority'] | |
24 | + def __str__(self): | |
25 | + return self.name | |
26 | + | |
27 | +class PhraseNegativity(models.Model): | |
28 | + name = models.CharField(max_length=16, unique=True) | |
29 | + priority = models.PositiveIntegerField() | |
30 | + class Meta: | |
31 | + ordering = ['priority'] | |
32 | + def __str__(self): | |
33 | + return self.name | |
34 | + | |
35 | +class PhraseInherentSie(models.Model): | |
36 | + name = models.CharField(max_length=16, unique=True) | |
37 | + priority = models.PositiveIntegerField() | |
38 | + class Meta: | |
39 | + ordering = ['priority'] | |
40 | + def __str__(self): | |
41 | + return self.name | |
42 | + | |
43 | +class AdverbialCategory(models.Model): | |
44 | + name = models.CharField(max_length=16, unique=True) | |
45 | + priority = models.PositiveIntegerField() | |
46 | + class Meta: | |
47 | + ordering = ['priority'] | |
48 | + def __str__(self): | |
49 | + return self.name | |
50 | + | |
51 | +# lex | |
52 | + | |
53 | +class Number(models.Model): | |
54 | + name = models.CharField(max_length=16, unique=True) | |
55 | + priority = models.PositiveIntegerField() | |
56 | + class Meta: | |
57 | + ordering = ['priority'] | |
58 | + def __str__(self): | |
59 | + return self.name | |
60 | + | |
61 | +class Gender(models.Model): | |
62 | + name = models.CharField(max_length=16, unique=True) | |
63 | + priority = models.PositiveIntegerField() | |
64 | + class Meta: | |
65 | + ordering = ['priority'] | |
66 | + def __str__(self): | |
67 | + return self.name | |
68 | + | |
69 | +class Degree(models.Model): | |
70 | + name = models.CharField(max_length=16, unique=True) | |
71 | + priority = models.PositiveIntegerField() | |
72 | + class Meta: | |
73 | + ordering = ['priority'] | |
74 | + def __str__(self): | |
75 | + return self.name | |
76 | + | |
77 | +# --- Lexicographically sorted ------------------------------------------------- | |
78 | + | |
79 | +class ComplexPreposition(models.Model): | |
80 | + name = models.CharField(max_length=16, unique=True) | |
81 | + class Meta: | |
82 | + ordering = ['name'] | |
83 | + def __str__(self): | |
84 | + return self.name | |
85 | + | |
86 | +class Preposition(models.Model): | |
87 | + name = models.CharField(max_length=16, unique=True) | |
88 | + class Meta: | |
89 | + ordering = ['name'] | |
90 | + def __str__(self): | |
91 | + return self.name | |
92 | + | |
93 | +class CPType(models.Model): | |
94 | + name = models.CharField(max_length=16, unique=True) | |
95 | + class Meta: | |
96 | + ordering = ['name'] | |
97 | + def __str__(self): | |
98 | + return self.name | |
99 | + | |
100 | +class CPRealisation(models.Model): | |
101 | + name = models.CharField(max_length=16, unique=True) | |
102 | + class Meta: | |
103 | + ordering = ['name'] | |
104 | + def __str__(self): | |
105 | + return self.name | |
106 | + | |
107 | +class ComparCategory(models.Model): | |
108 | + name = models.CharField(max_length=16, unique=True) | |
109 | + class Meta: | |
110 | + ordering = ['name'] | |
111 | + def __str__(self): | |
112 | + return self.name | |
113 | + | |
114 | +# === Phrase attribute objects ================================================= | |
115 | + | |
116 | +class PhraseTypeAttributes(models.Model): | |
117 | + pass | |
118 | + #class Meta: | |
119 | + # abstract = True | |
120 | + | |
121 | +class LexPhraseTypeAttributes(PhraseTypeAttributes): | |
122 | + pass | |
123 | + #class Meta: | |
124 | + # abstract = True | |
125 | + | |
126 | +# TODO a better solution for possp, E, qub etc.? | |
127 | +class EmptyAttributes(PhraseTypeAttributes): | |
128 | + empty = models.TextField(unique=True, null=False) | |
129 | + | |
130 | + def __str__(self): | |
131 | + return '()' | |
132 | + | |
133 | +#------------------------------------------------------------------------------- | |
134 | + | |
135 | +class NPAttributes(PhraseTypeAttributes): | |
136 | + case = models.OneToOneField(Case, on_delete=models.PROTECT) | |
137 | + | |
138 | + def __str__(self): | |
139 | + return '({})'.format(self.case) | |
140 | + | |
141 | +class LexNPAttributes(LexPhraseTypeAttributes): | |
142 | + num = models.OneToOneField(Number, on_delete=models.PROTECT) | |
143 | + | |
144 | + def __str__(self): | |
145 | + return '({})'.format(self.num) | |
146 | + | |
147 | +#------------------------------------------------------------------------------- | |
148 | + | |
149 | +class NumPAttributes(PhraseTypeAttributes): | |
150 | + case = models.OneToOneField(Case, on_delete=models.PROTECT) | |
151 | + | |
152 | + def __str__(self): | |
153 | + return '({})'.format(self.case) | |
154 | + | |
155 | +#------------------------------------------------------------------------------- | |
156 | + | |
157 | +class AdjPAttributes(PhraseTypeAttributes): | |
158 | + case = models.OneToOneField(Case, on_delete=models.PROTECT) | |
159 | + | |
160 | + def __str__(self): | |
161 | + return '({})'.format(self.case) | |
162 | + | |
163 | +class LexAdjPAttributes(LexPhraseTypeAttributes): | |
164 | + num = models.ForeignKey(Number, on_delete=models.PROTECT) | |
165 | + gend = models.ForeignKey(Gender, on_delete=models.PROTECT) | |
166 | + deg = models.ForeignKey(Degree, on_delete=models.PROTECT) | |
167 | + | |
168 | + class Meta: | |
169 | + unique_together = ['num', 'gend', 'deg'] | |
170 | + | |
171 | + def __str__(self): | |
172 | + return '({},{},{})'.format(self.num, self.gend, self.deg) | |
173 | + | |
174 | +#------------------------------------------------------------------------------- | |
175 | + | |
176 | +class PPasPAttributes(PhraseTypeAttributes): | |
177 | + case = models.OneToOneField(Case, on_delete=models.PROTECT) | |
178 | + | |
179 | + def __str__(self): | |
180 | + return '({})'.format(self.case) | |
181 | + | |
182 | +class LexPPasPAttributes(LexPhraseTypeAttributes): | |
183 | + num = models.ForeignKey(Number, on_delete=models.PROTECT) | |
184 | + gend = models.ForeignKey(Gender, on_delete=models.PROTECT) | |
185 | + neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT) | |
186 | + | |
187 | + class Meta: | |
188 | + unique_together = ['num', 'gend', 'neg'] | |
189 | + | |
190 | + def __str__(self): | |
191 | + return '({},{},{})'.format(self.num, self.gend, self.neg) | |
192 | + | |
193 | +#------------------------------------------------------------------------------- | |
194 | + | |
195 | +class PrepNPAttributes(PhraseTypeAttributes): | |
196 | + case = models.ForeignKey(Case, on_delete=models.PROTECT) | |
197 | + prep = models.ForeignKey(Preposition, on_delete=models.PROTECT) | |
198 | + | |
199 | + class Meta: | |
200 | + unique_together = ['case', 'prep'] | |
201 | + | |
202 | + def __str__(self): | |
203 | + return '({},{})'.format(self.prep, self.case) | |
204 | + | |
205 | +class LexPrepNPAttributes(LexPhraseTypeAttributes): | |
206 | + num = models.OneToOneField(Number, on_delete=models.PROTECT) | |
207 | + | |
208 | + def __str__(self): | |
209 | + return '({})'.format(self.num) | |
210 | + | |
211 | +#------------------------------------------------------------------------------- | |
212 | + | |
213 | +class ComPrepNPAttributes(PhraseTypeAttributes): | |
214 | + comprep = models.OneToOneField(ComplexPreposition, on_delete=models.PROTECT) | |
215 | + | |
216 | + def __str__(self): | |
217 | + return '({})'.format(self.comprep) | |
218 | + | |
219 | +#------------------------------------------------------------------------------- | |
220 | + | |
221 | +class PrepNumPAttributes(PhraseTypeAttributes): | |
222 | + case = models.ForeignKey(Case, on_delete=models.PROTECT) | |
223 | + prep = models.ForeignKey(Preposition, on_delete=models.PROTECT) | |
224 | + | |
225 | + class Meta: | |
226 | + unique_together = ['case', 'prep'] | |
227 | + | |
228 | + def __str__(self): | |
229 | + return '({},{})'.format(self.prep, self.case) | |
230 | + | |
231 | +#------------------------------------------------------------------------------- | |
232 | + | |
233 | +class PrepAdjPAttributes(PhraseTypeAttributes): | |
234 | + case = models.ForeignKey(Case, on_delete=models.PROTECT) | |
235 | + prep = models.ForeignKey(Preposition, on_delete=models.PROTECT) | |
236 | + | |
237 | + class Meta: | |
238 | + unique_together = ['case', 'prep'] | |
239 | + | |
240 | + def __str__(self): | |
241 | + return '({},{})'.format(self.prep, self.case) | |
242 | + | |
243 | +class LexPrepAdjPAttributes(LexPhraseTypeAttributes): | |
244 | + num = models.ForeignKey(Number, on_delete=models.PROTECT) | |
245 | + gend = models.ForeignKey(Gender, on_delete=models.PROTECT) | |
246 | + deg = models.ForeignKey(Degree, on_delete=models.PROTECT) | |
247 | + | |
248 | + class Meta: | |
249 | + unique_together = ['num', 'gend', 'deg'] | |
250 | + | |
251 | + def __str__(self): | |
252 | + return '({},{},{})'.format(self.num, self.gend, self.deg) | |
253 | + | |
254 | +#------------------------------------------------------------------------------- | |
255 | + | |
256 | +class PrepPPasPAttributes(PhraseTypeAttributes): | |
257 | + case = models.ForeignKey(Case, on_delete=models.PROTECT) | |
258 | + prep = models.ForeignKey(Preposition, on_delete=models.PROTECT) | |
259 | + | |
260 | + class Meta: | |
261 | + unique_together = ['case', 'prep'] | |
262 | + | |
263 | + def __str__(self): | |
264 | + return '({},{})'.format(self.prep, self.case) | |
265 | + | |
266 | +class LexPrepPPasPAttributes(LexPhraseTypeAttributes): | |
267 | + num = models.ForeignKey(Number, on_delete=models.PROTECT) | |
268 | + gend = models.ForeignKey(Gender, on_delete=models.PROTECT) | |
269 | + neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT) | |
270 | + | |
271 | + class Meta: | |
272 | + unique_together = ['num', 'gend', 'neg'] | |
273 | + | |
274 | + def __str__(self): | |
275 | + return '({},{},{})'.format(self.num, self.gend, self.neg) | |
276 | + | |
277 | +#------------------------------------------------------------------------------- | |
278 | + | |
279 | +class PrepGerPAttributes(PhraseTypeAttributes): | |
280 | + case = models.ForeignKey(Case, on_delete=models.PROTECT) | |
281 | + prep = models.ForeignKey(Preposition, on_delete=models.PROTECT) | |
282 | + | |
283 | + class Meta: | |
284 | + unique_together = ['case', 'prep'] | |
285 | + | |
286 | + def __str__(self): | |
287 | + return '({},{})'.format(self.prep, self.case) | |
288 | + | |
289 | +class LexPrepGerPAttributes(LexPhraseTypeAttributes): | |
290 | + num = models.ForeignKey(Number, on_delete=models.PROTECT) | |
291 | + neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT) | |
292 | + inhsie = models.ForeignKey(PhraseInherentSie, on_delete=models.PROTECT) | |
293 | + | |
294 | + class Meta: | |
295 | + unique_together = ['num', 'neg', 'inhsie'] | |
296 | + | |
297 | + def __str__(self): | |
298 | + return '({},{})'.format(self.num, self.neg, self.inhsie) | |
299 | + | |
300 | +#------------------------------------------------------------------------------- | |
301 | + | |
302 | +class CPAttributes(PhraseTypeAttributes): | |
303 | + cptype = models.ForeignKey(CPType, on_delete=models.PROTECT) | |
304 | + cpreals = models.ManyToManyField(CPRealisation) | |
305 | + | |
306 | + def __str__(self): | |
307 | + return '({},{})'.format(self.cptype, self.cpreals) | |
308 | + | |
309 | +class LexCPAttributes(LexPhraseTypeAttributes): | |
310 | + neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT) | |
311 | + inhsie = models.ForeignKey(PhraseInherentSie, on_delete=models.PROTECT) | |
312 | + | |
313 | + class Meta: | |
314 | + unique_together = ['neg', 'inhsie'] | |
315 | + | |
316 | + def __str__(self): | |
317 | + return '({},{})'.format(self.neg, self.inhsie) | |
318 | + | |
319 | +#------------------------------------------------------------------------------- | |
320 | + | |
321 | +class NCPAttributes(PhraseTypeAttributes): | |
322 | + case = models.ForeignKey(Case, on_delete=models.PROTECT) | |
323 | + cptype = models.ForeignKey(CPType, on_delete=models.PROTECT) | |
324 | + cpreals = models.ManyToManyField(CPRealisation) | |
325 | + | |
326 | + def __str__(self): | |
327 | + return '({},{},{})'.format(self.case, self.cptype,self.cpreals) | |
328 | + | |
329 | +class LexNCPAttributes(LexPhraseTypeAttributes): | |
330 | + neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT) | |
331 | + inhsie = models.ForeignKey(PhraseInherentSie, on_delete=models.PROTECT) | |
332 | + | |
333 | + class Meta: | |
334 | + unique_together = ['neg', 'inhsie'] | |
335 | + | |
336 | + def __str__(self): | |
337 | + return '({},{})'.format(self.neg, self.inhsie) | |
338 | + | |
339 | +#------------------------------------------------------------------------------- | |
340 | + | |
341 | +class PrepNCPAttributes(PhraseTypeAttributes): | |
342 | + case = models.ForeignKey(Case, on_delete=models.PROTECT) | |
343 | + prep = models.ForeignKey(Preposition, on_delete=models.PROTECT) | |
344 | + cptype = models.ForeignKey(CPType, on_delete=models.PROTECT) | |
345 | + cpreals = models.ManyToManyField(CPRealisation) | |
346 | + | |
347 | + def __str__(self): | |
348 | + return '({},{},{},{})'.format(self.prep, self.case, self.cptype, self.cpreals) | |
349 | + | |
350 | +#------------------------------------------------------------------------------- | |
351 | + | |
352 | +class InfPAttributes(PhraseTypeAttributes): | |
353 | + aspect = models.OneToOneField(PhraseAspect, on_delete=models.PROTECT) | |
354 | + | |
355 | + def __str__(self): | |
356 | + return '({})'.format(self.aspect) | |
357 | + | |
358 | +class LexInfPAttributes(LexPhraseTypeAttributes): | |
359 | + neg = models.ForeignKey(PhraseNegativity, on_delete=models.PROTECT) | |
360 | + inhsie = models.ForeignKey(PhraseInherentSie, on_delete=models.PROTECT) | |
361 | + | |
362 | + class Meta: | |
363 | + unique_together = ['neg', 'inhsie'] | |
364 | + | |
365 | + def __str__(self): | |
366 | + return '({},{})'.format(self.neg, self.inhsie) | |
367 | + | |
368 | +#------------------------------------------------------------------------------- | |
369 | + | |
370 | +class XPAttributes(PhraseTypeAttributes): | |
371 | + advcat = models.OneToOneField(AdverbialCategory, on_delete=models.PROTECT) | |
372 | + | |
373 | + def __str__(self): | |
374 | + return '({})'.format(self.advcat) | |
375 | + | |
376 | +class LexXPAttributes(LexPhraseTypeAttributes): | |
377 | + lex = models.OneToOneField('PhraseType', on_delete=models.PROTECT) | |
378 | + | |
379 | + def __str__(self): | |
380 | + return '({})'.format(self.lex) | |
381 | + | |
382 | +#------------------------------------------------------------------------------- | |
383 | + | |
384 | +class AdvPAttributes(PhraseTypeAttributes): | |
385 | + advcat = models.OneToOneField(AdverbialCategory, on_delete=models.PROTECT) | |
386 | + | |
387 | + def __str__(self): | |
388 | + return '({})'.format(self.advcat) | |
389 | + | |
390 | +class LexAdvPAttributes(LexPhraseTypeAttributes): | |
391 | + deg = models.OneToOneField(Degree, on_delete=models.PROTECT) | |
392 | + | |
393 | + def __str__(self): | |
394 | + return '({})'.format(self.deg) | |
395 | + | |
396 | +#------------------------------------------------------------------------------- | |
397 | + | |
398 | +class ComparAttributes(PhraseTypeAttributes): | |
399 | + comparcat = models.OneToOneField(ComparCategory, on_delete=models.PROTECT) | |
400 | + | |
401 | + def __str__(self): | |
402 | + return '({})'.format(self.comparcat) | |
403 | + | |
404 | +class LexComparAttributes(LexPhraseTypeAttributes): | |
405 | + lexes = models.ManyToManyField('PhraseType') | |
406 | + | |
407 | + def __str__(self): | |
408 | + return '({})'.format('+'.join(map(str, self.lexes.all()))) | |
409 | + | |
410 | +#------------------------------------------------------------------------------- | |
411 | + | |
412 | +class FixedAttributes(PhraseTypeAttributes): | |
413 | + phrase = models.ForeignKey('PhraseType', on_delete=models.PROTECT) | |
414 | + text = models.CharField(max_length=64) | |
415 | + #text = models.CharField(max_length=64, unique=True) | |
416 | + | |
417 | + # TODO: the text should be unique, but it requires corrections in Walenty | |
418 | + class Meta: | |
419 | + unique_together = ['phrase', 'text'] | |
420 | + | |
421 | + def __str__(self): | |
422 | + return '({},{})'.format(self.phrase, self.text) | |
423 | + | |
424 | +# ------------------------------------------------------------------------------ | |
425 | + | |
426 | + | |
427 | +# === Phrase objects =========================================================== | |
428 | + | |
429 | +class PhraseType(models.Model): | |
430 | + # osobne wpisy dla występujących ograniczonych realizacji, np. xp(locat[prepnp(u,gen),prepnp(w,loc)] | |
431 | + main_type = models.ForeignKey('PhraseTypeModel', related_name='main_phrase_types', on_delete=models.PROTECT) | |
432 | + attributes = models.ForeignKey(PhraseTypeAttributes, on_delete=models.PROTECT) | |
433 | + lexicalized_phrase = models.ForeignKey('PhraseType', related_name='lex_phrases', null=True, | |
434 | + on_delete=models.PROTECT) | |
435 | + text_rep = models.TextField(unique=True) | |
436 | + | |
437 | + class Meta: | |
438 | + ordering = ['main_type__priority'] | |
439 | + | |
440 | + def __str__(self): | |
441 | + return self.text_rep | |
442 | + | |
443 | + | |
444 | +class PhraseTypeModel(models.Model): | |
445 | + name = models.CharField(max_length=16, unique=True) | |
446 | + phraseologic = models.BooleanField() | |
447 | + priority = models.PositiveIntegerField() | |
448 | + | |
449 | + class Meta: | |
450 | + #ordering = ['priority'] | |
451 | + # TODO define an ordering priority for this model | |
452 | + ordering = ['name'] | |
453 | + | |
454 | + def __str__(self): | |
455 | + return self.name | |
... | ... |