Commit ebddb9db0e9e0a54835e56673b8ba10c22b14c66

Authored by Tomasz Bartosiak
2 parents 9b1356f1 3a02eb61
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=&#39;nom&#39;, 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
... ... @@ -229,6 +229,7 @@ FORM_MAP = {
229 229 OVERRIDE = {
230 230 'hopel' : ('hopla', 'subst:sg:gen:m3'),
231 231 'łupień' : ('łupnia', 'subst:sg:gen:m3'),
  232 + # kropka nad „i”
232 233 'i' : ('„i”', 'subst:sg:inst:n'),
233 234 }
234 235  
... ...
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">&times;</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
... ...