From 2a2e5ee1e7c3aa2d837380385e213316e1796535 Mon Sep 17 00:00:00 2001 From: dcz <dcz@ipipan.waw.pl> Date: Thu, 15 Dec 2022 11:36:43 +0100 Subject: [PATCH] First version of unified frame filtering - enable filtering of entry list based on frame filtering - creating filtering form for unified frame filtering - enable filtering of unified frame list based on unified frame filtering --- entries/forms.py | 48 +++++++++++++++++++ entries/static/entries/js/entries.js | 2 + entries/static/entries/js/forms.js | 7 +++ .../checkboxselectmultiple_with_tooltips.html | 2 +- entries/templates/entries_base.html | 19 ++++++++ .../role_checkboxselectmultiple_inner.html | 2 +- entries/urls.py | 1 + entries/views.py | 44 +++++++++++++---- .../unification/Entries/EntriesList.vue | 2 +- .../Unification/UnificationFramesList.vue | 13 +++++ frontend/src/main.js | 2 +- unifier/views.py | 11 ++++- 12 files changed, 139 insertions(+), 14 deletions(-) diff --git a/entries/forms.py b/entries/forms.py index c30edf0..2384da9 100644 --- a/entries/forms.py +++ b/entries/forms.py @@ -27,6 +27,7 @@ from semantics.models import ( ) from meanings.models import Synset +from unifier.models import UnifiedFrame from .form_fields.generic_fields import ( RangeFilter, @@ -601,6 +602,53 @@ class FrameFormFactory(FormFactory): raise KeyError(type(child_form)) + +class UnifiedFrameFormFactory(FormFactory): + + form_class_name = 'UnifiedFrameForm' + form_model = UnifiedFrame + form_formtype = 'unifiedframe' + form_header = _('Zunifikowana rama semantyczna') + + field_makers = ( + ( + 'opinion', + lambda: ModelMultipleChoiceFilter( + label=_('Opinia'), + queryset=FrameOpinion.objects.exclude(key='unk').filter(frame__isnull=False).distinct(), + key='key', + human_values=polish_strings.FRAME_OPINION(), + lookup='opinion', + ), None, + ), + ( + 'num_arguments', + lambda: RangeFilter( + label=_('Liczba argumentów'), + lookup='arguments_count', + ), None + ), + ( + 'name', + lambda: RegexFilter( + label=_('Nazwa'), + max_length=200, + lookup='title', + ), None, + ), + ( + None, None, + lambda n, cls: and_or_form_creator(_('UnifiedFrameArgument'), 'add-argument-{}'.format(n), data_add='argument'), + ), + ) + + @staticmethod + def get_child_form_prefix(child_form): + if child_form.model_class == Argument: + return 'arguments__in' + raise KeyError(type(child_form)) + + class ArgumentFormFactory(FormFactory): form_class_name = 'ArgumentForm' diff --git a/entries/static/entries/js/entries.js b/entries/static/entries/js/entries.js index a218b24..cc480ad 100644 --- a/entries/static/entries/js/entries.js +++ b/entries/static/entries/js/entries.js @@ -1128,6 +1128,8 @@ $(document).ready(function() { initialize_frames_form(); initialize_schemata_form(); + + initialize_unified_frames_form(); //$('.local-filter').hide(); diff --git a/entries/static/entries/js/forms.js b/entries/static/entries/js/forms.js index b123492..cacca0d 100644 --- a/entries/static/entries/js/forms.js +++ b/entries/static/entries/js/forms.js @@ -427,6 +427,7 @@ function initialize_main_form() { if (response.errors) { show_form_errors(main_form, response.errors); } else if (response.success) { + //ponizsza funkcja prowadzi do metody osadzonej w main.js, gdzie nastepuje odpowiednie odswiezenie komponentu z lista elementow Vue update_entries(); $('#entry-filters').modal('hide'); } @@ -476,6 +477,7 @@ function initialize_local_form(selector, url) { if (response.errors) { show_form_errors(form, response.errors); } else if (response.success) { + //ponizsza funkcja prowadzi do metody osadzonej w main.js, gdzie nastepuje odpowiednie odswiezenie komponentu z lista elementow Vue update_entries(); selector.modal('hide'); } @@ -494,6 +496,11 @@ function initialize_frames_form() { initialize_local_form($('#frame-filters'), '/' + lang + '/entries/send_frames_form/'); } +function initialize_unified_frames_form() { + initialize_local_form($('#unified-frame-filters'), '/' + lang + '/entries/send_unified_frames_form/'); +} + + function initialize_schemata_form() { initialize_local_form($('#schema-filters'), '/' + lang + '/entries/send_schemata_form/'); diff --git a/entries/templates/checkboxselectmultiple_with_tooltips.html b/entries/templates/checkboxselectmultiple_with_tooltips.html index b0cbaef..9ef9c8a 100644 --- a/entries/templates/checkboxselectmultiple_with_tooltips.html +++ b/entries/templates/checkboxselectmultiple_with_tooltips.html @@ -9,7 +9,7 @@ <input type="checkbox" class="{%if use_custom_control%}custom-control-input{% else %}form-check-input{% endif %}{%if is_bound %} is-{% if field.errors %}in{%endif%}valid{% endif %}"{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|default_if_none:""|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}> <label class="{%if use_custom_control%}custom-control-label{% else %}form-check-label{% endif %}" for="id_{{ field.html_name }}_{{ forloop.counter }}"> {% if choice.1.1 %} - {{ choice.1.0|unlocalize }} <span data-toggle="tooltip" data-placement="bottom" title="{{ choice.1.1 }}"><img src="{% static 'common/img/info.svg' %}" alt="info" width="14" height="14"/></span> + {{ choice.1.0|unlocalize }} <span data-toggle="tooltip" data-placement="bottom" title="{{ choice.1.1 }}"><img src="common/img/info.svg" alt="info" width="14" height="14"/></span> {% else %} {{ choice.1.0|unlocalize }} {% endif %} diff --git a/entries/templates/entries_base.html b/entries/templates/entries_base.html index b46559a..546dee8 100644 --- a/entries/templates/entries_base.html +++ b/entries/templates/entries_base.html @@ -42,6 +42,9 @@ <a href="#" class="dropdown-item font-weight-bold text-dark text-uppercase" id="filter-frames-button" data-toggle="modal" data-target="#frame-filters"> {% trans "Ramy" %} </a> + <a href="#" class="dropdown-item font-weight-bold text-dark text-uppercase" id="filter-unified-frames-button" data-toggle="modal" data-target="#unified-frame-filters"> + {% trans "Ramy zunifikowane" %} + </a> <a href="#" class="dropdown-item font-weight-bold text-dark text-uppercase" id="filter-schemata-button" data-toggle="modal" data-target="#schema-filters"> {% trans "Schematy" %} </a> @@ -115,6 +118,22 @@ </div> </div> +<div class="modal fade" id="unified-frame-filters" tabindex="-1" role="dialog" aria-labelledby="unified-frame-filtersLabel" aria-hidden="true"> + <div class="modal-dialog modal-xl" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="unified-frame-filtersLabel">{% trans "Filtrowanie zunifikowanych ram" %}</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body text-dark"> + {% crispy unified_frames_form %} + </div> + </div> + </div> +</div> + <div class="modal fade" id="schema-filters" tabindex="-1" role="dialog" aria-labelledby="schema-filtersLabel" aria-hidden="true"> <div class="modal-dialog modal-xl" role="document"> <div class="modal-content"> diff --git a/entries/templates/role_checkboxselectmultiple_inner.html b/entries/templates/role_checkboxselectmultiple_inner.html index 6310fe0..bc10a42 100644 --- a/entries/templates/role_checkboxselectmultiple_inner.html +++ b/entries/templates/role_checkboxselectmultiple_inner.html @@ -26,7 +26,7 @@ <input type="checkbox" class="{%if use_custom_control%}custom-control-input{% else %}form-check-input{% endif %}{%if is_bound %} is-{% if field.errors %}in{%endif%}valid{% endif %}"{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|default_if_none:""|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.parentloop.parentloop.counter }}_{{ forloop.parentloop.counter }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}> <label class="{%if use_custom_control%}custom-control-label{% else %}form-check-label{% endif %} text-dark" for="id_{{ field.html_name }}_{{ forloop.parentloop.parentloop.counter }}_{{ forloop.parentloop.counter }}_{{ forloop.counter }}"> {% if choice.1.1 %} - {{ choice.1.0|unlocalize }} <span data-toggle="tooltip" data-placement="bottom" title="{{ choice.1.1 }}"><img src="{% static 'common/img/info.svg' %}" alt="info" width="14" height="14"/></span> + {{ choice.1.0|unlocalize }} <span data-toggle="tooltip" data-placement="bottom" title="{{ choice.1.1 }}"><img src="common/img/info.sv" alt="info" width="14" height="14"/></span> {% else %} {{ choice.1.0|unlocalize }} {% endif %} diff --git a/entries/urls.py b/entries/urls.py index d735f33..3b4bd7c 100644 --- a/entries/urls.py +++ b/entries/urls.py @@ -10,6 +10,7 @@ urlpatterns = [ path('send_form/', views.send_form, name='send_form'), path('send_schemata_form/', views.send_schemata_form, name='send_schemata_form'), path('send_frames_form/', views.send_frames_form, name='send_frames_form'), + path('send_unified_frames_form/', views.send_unified_frames_form, name='send_unified_frames_form'), #path('filter_schemata/', views.filter_schemata, name='filter_schemata'), path('get_entries/', views.get_entries, name='get_entries'), path('get_entry/', views.get_entry, name='get_entry'), diff --git a/entries/views.py b/entries/views.py index 5430b01..8cacf58 100644 --- a/entries/views.py +++ b/entries/views.py @@ -38,7 +38,7 @@ from .forms import ( ArgumentFormFactory, PredefinedPreferenceFormFactory, RelationalPreferenceFormFactory, - SynsetPreferenceFormFactory, + SynsetPreferenceFormFactory, UnifiedFrameFormFactory, ) from .polish_strings import STATUS, POS, SCHEMA_OPINION, FRAME_OPINION, EXAMPLE_SOURCE, EXAMPLE_OPINION, RELATION @@ -66,7 +66,8 @@ def entries(request): 'is_vue_app': True, 'entries_form' : EntryForm(), 'frames_form' : FrameFormFactory.get_form(as_subform=False), - 'schemata_form' : SchemaFormFactory.get_form(as_subform=False) + 'schemata_form' : SchemaFormFactory.get_form(as_subform=False), + 'unified_frames_form': UnifiedFrameFormFactory.get_form(as_subform=False), }) @@ -80,7 +81,8 @@ def unification(request): 'unified_frame_id': request.GET.get("unified_frame_id"), 'entries_form' : EntryForm(), 'frames_form': FrameFormFactory.get_form(as_subform=False), - 'schemata_form': SchemaFormFactory.get_form(as_subform=False) + 'schemata_form': SchemaFormFactory.get_form(as_subform=False), + 'unified_frames_form': UnifiedFrameFormFactory.get_form(as_subform=False), }, ) @@ -95,6 +97,7 @@ FORM_FACTORY_TYPES = { 'phrase_lex' : LexFormFactory, 'lemma' : LemmaFormFactory, 'frame' : FrameFormFactory, + 'unifiedframe' : UnifiedFrameFormFactory, 'argument' : ArgumentFormFactory, 'predefined' : PredefinedPreferenceFormFactory, 'relational' : RelationalPreferenceFormFactory, @@ -334,6 +337,21 @@ def send_frames_form(request): return JsonResponse({}) +@ajax_required +def send_unified_frames_form(request): + if request.method == 'POST': + errors_dict = dict() + forms = collect_forms(request.POST['forms[]'], errors_dict) + eid = request.POST['entry'] + if errors_dict: + del request.session['unified_frame_form'] + return JsonResponse({ 'success' : 0, 'errors' : errors_dict }) + else: + request.session['unified_frame_form'] = request.POST['forms[]'] + return JsonResponse({ 'success' : 1 }) + return JsonResponse({}) + + def get_scroller_params(POST_data): order = (int(POST_data['order[0][column]']), POST_data['order[0][dir]']) return { @@ -371,6 +389,12 @@ def get_entries(request): if scroller_params['filter']: entries = entries.filter(name__startswith=scroller_params['filter']) filtered = entries.count() + + local_frame_form = None + if 'frame_form' in request.session: + errors_dict = dict() + local_frame_form = collect_forms(request.session['frame_form'], errors_dict) + assert(not errors_dict) linked_ids = set() if request.session['show_linked_entries']: @@ -402,28 +426,32 @@ def get_entries(request): .select_related('status', 'pos') .prefetch_related(Prefetch("assignments", to_attr="_assignments")) ) + if with_lexical_units: + frameQueryset = Frame.objects.select_related("slowal_frame_2_unified_frame").prefetch_related(Prefetch("assignments", to_attr="_assignments")); entries = entries.prefetch_related( Prefetch( "lexical_units", LexicalUnit.objects.prefetch_related( Prefetch( "frames", - Frame.objects - .select_related("slowal_frame_2_unified_frame") - .prefetch_related(Prefetch("assignments", to_attr="_assignments")), + queryset=get_filtered_objects(local_frame_form, frameQueryset) if local_frame_form is not None else frameQueryset, to_attr="_frames", ) ) ) ) + status_names = STATUS() POS_names = POS() def iter_lexical_units(e): for lu in e.lexical_units.all(): - lu._frame = lu._frames[0] if lu._frames else None - yield lu + lu._frame = lu._frames[0] if lu._frames and len(lu._frames) > 0 else None + if lu._frame is None: + continue + else: + yield lu result = { 'draw' : scroller_params['draw'], diff --git a/frontend/src/components/unification/Entries/EntriesList.vue b/frontend/src/components/unification/Entries/EntriesList.vue index 40e4b3c..e72897d 100644 --- a/frontend/src/components/unification/Entries/EntriesList.vue +++ b/frontend/src/components/unification/Entries/EntriesList.vue @@ -24,7 +24,7 @@ } }, methods: { - reset_entry_list() { + reset_list() { this.$emit('lexicalUnitSelected', null, null); setup_entries_list({ table: this.$refs.table, diff --git a/frontend/src/components/unification/Unification/UnificationFramesList.vue b/frontend/src/components/unification/Unification/UnificationFramesList.vue index b4e1bb4..933bae2 100644 --- a/frontend/src/components/unification/Unification/UnificationFramesList.vue +++ b/frontend/src/components/unification/Unification/UnificationFramesList.vue @@ -10,6 +10,18 @@ export default { canViewAssignment: has_permission("users.view_assignment") } }, + methods: { + reset_list() { + this.$emit('unifiedFrameSelected', null); + setup_frames_list({ + table: this.$refs.table, + unifiedFrameSelected: (unifiedFrameId) => { + this.$emit('unifiedFrameSelected', unifiedFrameId); + }, + selectEntryId: this.initialUnifiedFrameId, + }); + } + }, watch: { unificationEntriesListRefreshKey() { // TODO: reload data and click in selected row @@ -25,6 +37,7 @@ export default { }, emits: ['unifiedFrameSelected'], mounted() { + this.$.appContext.config.globalProperties.$entries_list = this; setup_frames_list({ table: this.$refs.table, unifiedFrameSelected: (unifiedFrameId) => { diff --git a/frontend/src/main.js b/frontend/src/main.js index 3e8ecc4..ffa91da 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -25,6 +25,6 @@ window.update_entries = function () { mounted = true; } if(app._context.config.globalProperties.$entries_list) { - app._context.config.globalProperties.$entries_list.reset_entry_list(); + app._context.config.globalProperties.$entries_list.reset_list(); } } diff --git a/unifier/views.py b/unifier/views.py index 036dc00..8de7272 100644 --- a/unifier/views.py +++ b/unifier/views.py @@ -8,7 +8,8 @@ from django.views.decorators.csrf import csrf_exempt from common.decorators import ajax_required, ajax from entries.polish_strings import EXAMPLE_SOURCE, EXAMPLE_OPINION -from entries.views import get_scroller_params, get_alternations, get_prefs_list, schema2dict, frame2dict +from entries.views import get_scroller_params, get_alternations, get_prefs_list, schema2dict, frame2dict, collect_forms, \ + get_filtered_objects from importer.unification.UnificationPreprocessXML import UnificationPreprocessHandler from meanings.models import LexicalUnit from semantics.choices import FrameStatus @@ -72,8 +73,14 @@ def get_unified_frames(request): exclude_status = request.GET.get('exclude_status') restrict_to_user = request.GET.get('restrict_to_user') + errors_dict = dict() + forms = collect_forms(request.session['unified_frame_form'], errors_dict) + res = {} - unifiedFrames = UnifiedFrame.objects.all(); + # unifiedFrames = UnifiedFrame.objects.all(); + + unifiedFrames = get_filtered_objects(forms).filter() + for unifiedFrame in unifiedFrames: res[unifiedFrame.id] = { 'id': str(unifiedFrame.id), -- GitLab