From cb2b913ac0dd45184c8fdd1b61ad4279959c6b79 Mon Sep 17 00:00:00 2001 From: dcz <dcz@ipipan.waw.pl> Date: Thu, 9 Nov 2023 12:00:49 +0100 Subject: [PATCH] Vertical relation first commit --- entries/static/entries/js/entries.js | 28 + entries/templates/entries_base.html | 3 + entries/templates/vertical_relations.html | 24 + entries/urls.py | 2 + entries/views.py | 23 + .../vertical_relations/VerticalRelation.vue | 73 ++ .../VerticalRelationEdit.vue | 629 ++++++++++++++++++ .../VerticalRelationElement.vue | 188 ++++++ .../VerticalRelationPreview.vue | 64 ++ .../VerticalRelationRightPane.vue | 66 ++ frontend/src/main.js | 2 + shellvalier/settings.py | 1 + shellvalier/urls.py | 1 + syntax/management/commands/import_tei.py | 19 + unifier/views.py | 19 +- vertical_relations/__init__.py | 0 vertical_relations/apps.py | 6 + .../management/commands/__init__.py | 0 .../commands/db_import_relation_entries.py | 15 + vertical_relations/models.py | 63 ++ vertical_relations/urls.py | 15 + vertical_relations/views.py | 128 ++++ 22 files changed, 1368 insertions(+), 1 deletion(-) create mode 100644 entries/templates/vertical_relations.html create mode 100644 frontend/src/components/unification/vertical_relations/VerticalRelation.vue create mode 100644 frontend/src/components/unification/vertical_relations/VerticalRelationEdit.vue create mode 100644 frontend/src/components/unification/vertical_relations/VerticalRelationElement.vue create mode 100644 frontend/src/components/unification/vertical_relations/VerticalRelationPreview.vue create mode 100644 frontend/src/components/unification/vertical_relations/VerticalRelationRightPane.vue create mode 100644 vertical_relations/__init__.py create mode 100644 vertical_relations/apps.py create mode 100644 vertical_relations/management/commands/__init__.py create mode 100644 vertical_relations/management/commands/db_import_relation_entries.py create mode 100644 vertical_relations/models.py create mode 100644 vertical_relations/urls.py create mode 100644 vertical_relations/views.py diff --git a/entries/static/entries/js/entries.js b/entries/static/entries/js/entries.js index 6d83ac0..da416ee 100644 --- a/entries/static/entries/js/entries.js +++ b/entries/static/entries/js/entries.js @@ -13,6 +13,8 @@ var roles = [] var role_attributes = [] var role_sub_attributes = [] var frame_opinions = [] +var main_vertical_relations = [] +var temporal_vertical_relations = [] function make_opinion_row(item, span, width) { const opinion_row = document.createElement('tr'); @@ -1145,6 +1147,28 @@ function getFrameOpinions() { }); } +function getMainVerticalRelations() { + $.ajax({ + dataType: "json", + url: '/' + lang + '/vertical_relations/ajax_main_vertical_relations', + success: function(data){ + main_vertical_relations = data.main_vertical_relations; + }, + async: false + }); +} + +function getTemporalVerticalRelations() { + $.ajax({ + dataType: "json", + url: '/' + lang + '/vertical_relations/ajax_temporal_vertical_relations', + success: function(data){ + temporal_vertical_relations = data.temporal_vertical_relations; + }, + async: false + }); +} + $(document).ready(function() { bind_last_visited(); @@ -1181,6 +1205,10 @@ $(document).ready(function() { getFrameOpinions(); + getMainVerticalRelations(); + + getTemporalVerticalRelations(); + // $.getJSON('relations', function(data){ // memorizeRelations(data.relations); // }); diff --git a/entries/templates/entries_base.html b/entries/templates/entries_base.html index 1788c91..2acdd29 100644 --- a/entries/templates/entries_base.html +++ b/entries/templates/entries_base.html @@ -35,6 +35,9 @@ class="nav-link cursor-pointer">{% trans "Hierarchia" %}</a></li> {% if is_superlexicograf %} <li class="nav-item mr-1"><a href="{% url 'entries:lu_free' %}" class="nav-link">{% trans "Wolne jednostki" %}</a></li> + <li class="nav-item mr-1"><a + onclick='window.location.replace(window.currUnifiedFrameId ? "/pl/entries/vertical-relations/?unified_frame_id="+window.currUnifiedFrameId : "/pl/entries/vertical-relations")' + class="nav-link cursor-pointer">{% trans "Relacie poziome" %}</a></li> {% endif %} {% endif %} <li class="nav-item dropdown mr-1"> diff --git a/entries/templates/vertical_relations.html b/entries/templates/vertical_relations.html new file mode 100644 index 0000000..61a407d --- /dev/null +++ b/entries/templates/vertical_relations.html @@ -0,0 +1,24 @@ +{% extends "entries_base.html" %} + +{% load i18n %} +{% load static %} + +{% block title %}{% trans "HasÅ‚a" %}{% endblock %} + +{% block scripts %} + {{ block.super }} + <link rel="stylesheet" type="text/css" href="{% static 'entries/css/unification_frames.css' %}"> + <link rel="stylesheet" type="text/css" href="{% static 'common/css/role_colours.css' %}"> + <script src="{% static 'entries/js/unification_entries_list.js' %}"></script> + <script src="{% static 'entries/js/unification_frames_list.js' %}"></script> + <script src="{% static 'entries/js/unification_entries_for_frames_list.js' %}"></script> + <script src="{% static 'entries/js/jquery-impromptu.min.js' %}"></script> + <script> + window.currUnifiedFrameId = {{ unified_frame_id|default:'null' }}; + </script> +{% endblock %} + +{% block modals %} + {{ block.super }} + <div id="lexical-unit-notes-template" class="d-none">{% include 'notes.html' %}</div> +{% endblock %} diff --git a/entries/urls.py b/entries/urls.py index f434ba6..9fd3101 100644 --- a/entries/urls.py +++ b/entries/urls.py @@ -19,6 +19,8 @@ urlpatterns = [ path('change_show_linked_entries/', views.change_show_linked_entries, name='change_show_linked_entries'), path('unification/', views.unification, name='unification'), path('hierarchy/', views.hierarchy, name='hierarchy'), + path('vertical-relations/', views.vertical_relations, name='vertical-relations'), + path('lu_free/', views.lu_free, name='lu_free'), path('autocomplete/', autocompletes.autocomplete, name='autocomplete'), diff --git a/entries/views.py b/entries/views.py index 013fc94..8bca9f3 100644 --- a/entries/views.py +++ b/entries/views.py @@ -138,6 +138,29 @@ def lu_free(request): ) +@login_required +def vertical_relations(request): + + unified_frame_id = None + if "unified_frame_id" in request.GET: + unified_frame_id = request.GET.get("unified_frame_id") + + user = request.user + return render( + request, + 'hierarchy.html', + { + 'is_vue_app': True, + 'unified_frame_id': unified_frame_id, + 'entries_form': EntryForm(), + 'frames_form': FrameFormFactory.get_form(as_subform=False), + 'schemata_form': SchemaFormFactory.get_form(as_subform=False), + 'unified_frames_form': UnifiedFrameFormFactory.get_form(as_subform=False), + 'is_superlexicograf': user.groups.filter(name=settings.SUPER_LEXICOGRAPHS_GROUP_NAME).exists() + }, + ) + + FORM_TYPES = { 'entry' : EntryForm, } diff --git a/frontend/src/components/unification/vertical_relations/VerticalRelation.vue b/frontend/src/components/unification/vertical_relations/VerticalRelation.vue new file mode 100644 index 0000000..ce8456e --- /dev/null +++ b/frontend/src/components/unification/vertical_relations/VerticalRelation.vue @@ -0,0 +1,73 @@ +<script> +import UnificationFramesList from "../Unification/UnificationFramesList.vue"; +import VerticalRelationRightPane from "./VerticalRelationRightPane.vue"; + +export default { + data () { + return { + entryId: null, + lexicalUnitId: null, + unifiedFrameId: null, + gettext: window.gettext, + unificationEntriesListRefreshKey: 1, + }; + }, + components: {VerticalRelationRightPane, UnificationFramesList}, + methods: { + lexicalUnitSelected (entryId, lexicalUnitId) { + this.entryId = entryId; + this.lexicalUnitId = lexicalUnitId; + }, + unifiedFrameSelected (unifiedFrameId, entryId, lexicalUnitId) { + this.unifiedFrameId = unifiedFrameId; + this.entryId = entryId; + this.lexicalUnitId = lexicalUnitId; + }, + refreshEntriesList() { + this.unificationEntriesListRefreshKey++; + } + }, + setup() { + const unified_frame_id = new URL(location.href).searchParams.get('unified_frame_id'); + return { + initial_unified_frame_id: parseInt(unified_frame_id), + }; + }, + mounted () { + this.unifiedFrameSelected(window.currUnifiedFrameId); + $('#entries-list').length && Split(['#entries-list', '#entry-display'], { + sizes: [20, 80], + gutterSize: 4, + minSize: 10, + elementStyle: (dimension, size, gutterSize) => { + return { + 'flex-basis': 'calc(' + size + '% - ' + gutterSize + 'px)' + } + }, + }); + }, +}; +</script> + +<template> + <div id="entries-list" class="col h-100 w-100 pr-0 overflow-hidden"> + <div id="entries-list-div" class="col p-0 h-100 w-100 overflow-hidden"> + <unification-frames-list + :unificationEntriesListRefreshKey="unificationEntriesListRefreshKey" + :initialUnifiedFrameId="initial_unified_frame_id" + :setupHierarchyMarking="true" + @unified-frame-selected="unifiedFrameSelected" + /> + </div> + </div> + <div id="entry-display" class="col h-100 p-0 overflow-hidden"> + <vertical-relation-right-pane + ref="hierarchyRightPane" + :entryId="entryId" + :lexicalUnitId="lexicalUnitId" + :initialUnifiedFrameId="unifiedFrameId" + @refresh-entries-list="refreshEntriesList" + /> + </div> +</template> + diff --git a/frontend/src/components/unification/vertical_relations/VerticalRelationEdit.vue b/frontend/src/components/unification/vertical_relations/VerticalRelationEdit.vue new file mode 100644 index 0000000..3ea0a31 --- /dev/null +++ b/frontend/src/components/unification/vertical_relations/VerticalRelationEdit.vue @@ -0,0 +1,629 @@ +<script> +import InfoTooltip from "../../shared/InfoTooltip.vue"; +import Spinner from "../../shared/Spinner.vue"; +import ExamplesComponent from "../shared/frame-components/ExamplesComponent.vue"; +import SlowalFrameComponent from "../shared/frame-components/SlowalFrameComponent.vue"; +import SemanticsSchemataComponent from "../shared/frame-components/SemanticsSchemataComponent.vue"; +import MeaningComponent from "../shared/frame-components/MeaningComponent.vue"; +import SelectionalPreference from "../Unification/SelectionalPreference.js"; +import { slowal_frames2selecional_preferencies } from "../shared/utils.js"; +import HierarchyPreview from "../hierarchy/HierarchyPreview.vue"; +import HierarchyElement from "../hierarchy/HierarchyElement.vue"; +import {frames2lexical_units, send_post_request} from "../shared/utils"; + + +export default { + props: { + unifiedFrameId: Number, + previewedUnifiedFrameId: Number, + forceRefresh: Number, + }, + data() { + return { + gettext: window.gettext, + unified_frame: {}, + unified_frame_title: '', + unified_frame_arguments: [], + active_unified_frame_argument: null, + slowal_frames2selecional_preferencies_mapping: {}, + lexical_units: [], + img_prefix: window.STATIC_URL, + frames: [], + right_pane_tabs: [ + {id: 'hierarchy', label: gettext('Hierarchia')}, + {id: 'notes', label: gettext('Notatki')}, + ], + right_pane_tab: 'hierarchy', + currentPreviewedUnifiedFrameId: this.previewedUnifiedFrameId, + internalForceRefresh: this.forceRefresh, + statusButtonTitle: '', + active_slowal_frame: null, + showVerifiedFrames: false, + subentries: null, + alternations: null, + realisation_phrases: null, + realisation_descriptions: null, + examples: null, + selectedFrameArguments: null, + frame_arguments_or_type: false, + selectedLus: null, + selectedSchemas: null, + selectedExamples: null, + hidden_frames: [], + target_unified_frame: null, + hierarchy_hyponyms: null, + hierarchy_hyperonyms: null, + current_vertical_relation: null, + vertical_relations: null, + } + }, + components: {HierarchyElement, InfoTooltip, Spinner, HierarchyPreview, SlowalFrameComponent, ExamplesComponent, SemanticsSchemataComponent, MeaningComponent}, + emits: ['goToDisplay', 'refresh', 'swapFrames', 'refreshEntriesList', 'clearUnifiedFrameView'], + watch: { + forceRefresh(newVal, oldVal) { + this.loadFrame(); + } + }, + computed: { + selectionalPreference() { + return new SelectionalPreference(); + }, + }, + methods: { + createHierarchyTree() { + return { + 'unified_frame': this.unified_frame, + 'hyponyms': this.hierarchy_hyponyms, + 'hypyronyms': this.hierarchy_hyperonyms + }; + }, + hasWhiteSpace(s) { + return /\s/g.test(s); + }, + async loadFrame() { + try { + const data = {'unified_frame_id': this.unifiedFrameId, 'no_filters' : false}; + $.ajax({ + type: 'post', + url: '/' + lang + '/unifier/get_unified_frame/', + dataType: 'json', + data: data, + timeout: 60000, + success: function (response) { + + this.img_prefix = window.STATIC_URL; + this.lexical_units = this.frames2lexical_units(response.frames); + this.unified_frame = response.unified_frame; + this.unified_frame_title = this.unified_frame.title; + this.unified_frame_arguments = this.unified_frame.arguments; + this.frames = response.frames; + this.vertical_relations = response.vertical_relations; + this.slowal_frames2selecional_preferencies_mapping = slowal_frames2selecional_preferencies(this.unified_frame, response.frames); + + this.subentries = response.subentries; + this.alternations = response.alternations; + this.realisation_phrases = response.realisation_phrases; + this.realisation_descriptions = response.realisation_descriptions; + this.examples = response.examples; + + this.fulfill_slowal_frames_arguments_with_empty_elems(response.unified_frame, response.frames) + window.update_last_visited(response.last_visited); + window.clear_info(); + + if (!this.active_slowal_frame) { + this.setup_notes_unified_frame(); + } + }.bind(this), + error: function (request, errorType, errorMessage) { + show_error(errorType + ' (' + errorMessage + ')'); + } + }); + this.loadHierarchy(); + } catch (error) { + console.log(error); + } + }, + loadHierarchy() { + $.ajax({ + type: 'post', + url: '/' + lang + '/unifier/get_hierarchy_hyponyms/'+this.unifiedFrameId+"/", + dataType: 'json', + timeout: 60000, + success: function (response) { + this.hierarchy_hyponyms = response.hyponyms; + }.bind(this), + error: function (request, errorType, errorMessage) { + show_error(errorType + ' (' + errorMessage + ')'); + } + }); + + $.ajax({ + type: 'post', + url: '/' + lang + '/unifier/get_hierarchy_hyperonyms/'+this.unifiedFrameId+"/", + dataType: 'json', + timeout: 60000, + success: function (response) { + this.hierarchy_hyperonyms = response.hyperonyms; + }.bind(this), + error: function (request, errorType, errorMessage) { + show_error(errorType + ' (' + errorMessage + ')'); + } + }); + }, + setup_notes_unified_frame() { + setup_notes($('#notes-component'), $('#lexical-unit-notes-template'), this.unified_frame.id, 'unifier.UnifiedFrame', + this.setup_notes_unified_frame, 'hierarchy_frame', true); + }, + unifiedFrameArgumentSelected(argument) { + if (this.active_unified_frame_argument === argument) { + this.active_unified_frame_argument = null; + this.frame_arguments_or_type = false; + this.deselectSlowalFrameSelectedElements(); + } else { + this.active_slowal_frame = null; + this.active_unified_frame_argument = argument; + const slowalFrameArguments = this.getSlowalFrameArgumentsBy(argument); + this.frame_arguments_or_type = true; + this.deselectSlowalFrameSelectedElements(); + this.selectedFrameArguments = slowalFrameArguments; + } + this.unifiedFrameArgumentHovered(argument); + }, + unifiedFrameArgumentHovered(argument) { + clear_info(); + if (argument && this.active_unified_frame_argument === argument) { + show_info(gettext('Kliknij, aby cofnąć wybór kolumny do edycji.')); + } + if (argument && this.active_unified_frame_argument !== argument) { + show_info(gettext('Kliknij, aby wybrać kolumnÄ™ do edycji.')); + } + }, + getSlowalFrameArgumentsBy(unified_frame_argument) { + const slowalFrameArgumentIds = []; + for (let i in this.unified_frame.slowal_frame_mapping) { + const slowal_frame_mapping = this.unified_frame.slowal_frame_mapping[i]; + const slowalFrame = this.frames.find(frame => frame.id === slowal_frame_mapping.slowal_frame_id); + if(slowalFrame != null) { + for (let j in slowal_frame_mapping.slowal_frame_argument_mapping) { + const slowal_frame_argument_mapping = slowal_frame_mapping.slowal_frame_argument_mapping[j]; + if (slowal_frame_argument_mapping.unified_frame_agrument_id == unified_frame_argument.id) { + const slowalFrameArgument = slowalFrame.arguments.find(arg => arg.argument_id === slowal_frame_argument_mapping.slowal_frame_agrument_id); + slowalFrameArgumentIds.push(slowalFrameArgument); + } + } + } + } + return slowalFrameArgumentIds; + }, + isSuperLeksykograf() { + return has_permission("users.view_assignment"); + }, + isFrameVerified(frame) { + const isSuperLeksykograf = this.isSuperLeksykograf(); + return (!isSuperLeksykograf && frame.status === 'G') || (isSuperLeksykograf && frame.status === 'S') + }, + select_slowal_frame_req(to_invoke) { + if (this.active_slowal_frame) { + to_invoke(); + } else { + alert(gettext("Wybierz ramÄ™, dla której chcesz zmienić status.")); + } + }, + isFrameVisible(status) { + return (status != 'B' && status != 'C') || this.isSuperLeksykograf(); + }, + deselectSlowalFrameSelectedElements() { + this.subentries.forEach(subentry => { + subentry.schemata.forEach(s => { + s.selected = false; + }); + }); + this.frames.forEach(frame => { + frame.lexical_units.forEach(lu => { + lu.selected = false; + }); + frame.arguments.forEach(argument => { + argument.selected = false; + }); + }); + this.examples.forEach(example => { + example.selected = false; + }); + this.selectedLus = []; + this.selectedFrameArguments = []; + this.selectedSchemas = []; + this.selectedExamples = []; + }, + slowalFrameSelected(frame) { + this.deselectSlowalFrameSelectedElements(); + if (this.active_slowal_frame === frame) { + this.active_slowal_frame = null; + } else { + this.active_slowal_frame = frame; + } + }, + isSelectedFrame(frame) { + if (this.active_slowal_frame) { + return frame.id === this.active_slowal_frame.id; + } else { + return false; + } + }, + changePreviewedUnifiedFrameId(unifiedFrameId) { + this.currentPreviewedUnifiedFrameId = unifiedFrameId; + }, + getArgumentCSS(argument) { + return (argument.role ? argument.role.str + ' ' : '') + (argument == this.active_unified_frame_argument ? 'active' : ''); + }, + schemataSelected(schemas) { + this.selectedSchemas = schemas; + }, + exampleSelected(selectedExamples) { + this.selectedExamples = selectedExamples; + }, + isReadOnlyForSuperLeksykograf() { + return (this.isSuperLeksykograf() && this.unified_frame.status === 'O') && this.unified_frame.assignee_username !== window.USER_USERNAME; + }, + frames2lexical_units(frames) { + const lexical_units = [] + for (let i in frames) { + const frame = frames[i]; + for (let j in frame.lexical_units) { + const lexical_unit = frame.lexical_units[j]; + lexical_unit.opinion = frame.opinion; + lexical_unit.opinion_key = frame.opinion_key; + lexical_unit.frame_status = frame.status; + lexical_unit.frame = frame; + lexical_units.push(lexical_unit); + } + } + return lexical_units; + }, + fulfill_slowal_frames_arguments_with_empty_elems(unified_frame, slowal_frames) { + for (let i in unified_frame.slowal_frame_mapping) { + const slowal_frame_mapping = unified_frame.slowal_frame_mapping[i]; + let slowal_frame = slowal_frames.find(o => o.id === slowal_frame_mapping.slowal_frame_id); + if(slowal_frame != null) { + let new_slowal_frame_arguments = []; + for (let j in unified_frame.arguments) { + const unified_frame_argument = unified_frame.arguments[j]; + let unified_frame_argument_mapping = slowal_frame_mapping.slowal_frame_argument_mapping.find(o => o.unified_frame_agrument_id === unified_frame_argument.id); + let slowal_frame_argument = null; + if (unified_frame_argument_mapping == null) { + slowal_frame_argument = { + 'str': 'Empty', + 'id': slowal_frame.id + '-_' + (unified_frame_argument.id), + 'role': 'Empty', + 'role_type': 'Empty', + 'preferences': [], + 'proposed_roles': [], + } + } else { + slowal_frame_argument = slowal_frame.arguments.find(o => o.argument_id === unified_frame_argument_mapping.slowal_frame_agrument_id); + } + new_slowal_frame_arguments.push(slowal_frame_argument) + } + slowal_frame.arguments = new_slowal_frame_arguments; + } + } + }, + createFrameHierarchyRepresentationHTML(unified_frame) { + + }, + createHierarchyHTML() { + this.unified_frame + }, + save_main_vertical_relation() { + const main_vertical_relation_select = function () { + return main_vertical_relations.map(frame_opinion => { + return `<label><input type="radio" name="opinion" value="${frame_opinion.id}" /> ${frame_opinion.name}</label><br />`; + }).join(""); + }.bind(this); + + const main_vertical_relation_select_popup = { + state0: { + title: 'Wybierz relacjÄ™ głównÄ…', + html: main_vertical_relation_select, + buttons: {Anuluj: 0, Wybierz: 1}, + focus: -1, + submit: function (e, v, m, f) { + if (v == 0) { + e.preventDefault(); + $.prompt.close(); + } + if (v === 1) { + e.preventDefault(); + let relation_id = normalizeFormData(f.opinion)[0]; + send_post_request('/vertical_relations/save_main_vertical_relation/', + { + 'source_unified_frame_id': this.unified_frame.id, + 'target_unified_frame_id': this.target_unified_frame ? this.target_unified_frame.id : null, + 'main_vertical_relation_id': relation_id + }, + (reponse) => { + show_info('Relacja główne zostaÅ‚a ustawiona.'); + this.current_vertical_relation = reponse['vertical_relation']; + this.loadFrame(); + $.prompt.close(); + }, + (request, errorType, errorMessage) => { + show_error(errorType + ' (' + errorMessage + ')'); + alert(gettext("Ustawienie relacji głównej nie powiodÅ‚a siÄ™. Błąd: " + errorType + ' (' + errorMessage + ')')); + $.prompt.close(); + }) + } + }.bind(this) + } + } + $.prompt(main_vertical_relation_select_popup); + }, + save_temporal_vertical_relation() { + if(this.current_vertical_relation) { + const temporal_vertical_relations_ids = new Set(this.current_vertical_relation.temporal_vertical_relation.map(l => l.id)); + const temporal_vertical_relation_select = function () { + return temporal_vertical_relations.map(relation => { + return `<label><input type="checkbox" ${temporal_vertical_relations_ids.has(relation.id) ? 'checked' : ''} name="opinion" value="${relation.id}" /> ${relation.name}</label><br />`; + }).join(""); + }.bind(this); + + + const temporal_vertical_relation_select_popup = { + state0: { + title: 'Wybierz relacje pomocnicze', + html: temporal_vertical_relation_select, + buttons: {Anuluj: 0, Wybierz: 1}, + focus: -1, + submit: function (e, v, m, f) { + if (v == 0) { + e.preventDefault(); + $.prompt.close(); + } + if (v === 1) { + e.preventDefault(); + let relation_ids = normalizeFormData(f.opinion); + send_post_request('/vertical_relations/save_temporal_vertical_relation/', + { + 'source_unified_frame_id': this.unified_frame.id, + 'target_unified_frame_id': this.target_unified_frame ? this.target_unified_frame.id : null, + 'temporal_vertical_relation_ids': JSON.stringify(relation_ids) + }, + (reponse) => { + show_info('Relacje pomocnicze zostaÅ‚y ustawione.'); + this.current_vertical_relation = reponse['vertical_relation']; + this.loadFrame(); + $.prompt.close(); + }, + (request, errorType, errorMessage) => { + show_error(errorType + ' (' + errorMessage + ')'); + alert(gettext("Ustawienie relacji pomocniczych nie powiodÅ‚a siÄ™. Błąd: " + errorType + ' (' + errorMessage + ')')); + $.prompt.close(); + }) + } + }.bind(this) + } + } + $.prompt(temporal_vertical_relation_select_popup); + } else { + alert(gettext("Dodanie relacji pomocniczych wymaga ustawienia relacji głównej.")); + } + }, + load_vertical_relation() { + const load_vertical_relation_select = function () { + return this.vertical_relations.map(vertical_relation => { + return `<label><input type="radio" name="opinion" value="${vertical_relation.id}" /> ${vertical_relation.str}</label><br />`; + }).join(""); + }.bind(this); + + const load_vertical_relation_select_popup = { + state0: { + title: 'Wybierz relacjÄ™ do zaÅ‚adowania', + html: load_vertical_relation_select, + buttons: {Anuluj: 0, Wybierz: 1}, + focus: -1, + submit: function (e, v, m, f) { + if (v == 0) { + e.preventDefault(); + $.prompt.close(); + } + if (v === 1) { + e.preventDefault(); + let relation_id = normalizeFormData(f.opinion)[0]; + send_post_request('/vertical_relations/load_vertical_relation/', + { + 'vertical_relation_id': relation_id, + }, + (reponse) => { + show_info('Relacja zostaÅ‚a zaÅ‚adowana.'); + this.current_vertical_relation = reponse['vertical_relation']; + this.loadFrame(); + $.prompt.close(); + }, + (request, errorType, errorMessage) => { + show_error(errorType + ' (' + errorMessage + ')'); + alert(gettext("Åadowanie relacji nie powiodÅ‚a siÄ™. Błąd: " + errorType + ' (' + errorMessage + ')')); + $.prompt.close(); + }) + } + }.bind(this) + } + } + $.prompt(load_vertical_relation_select_popup); + }, + remove_vertical_relation() { + if(this.current_vertical_relation) { + send_post_request('/vertical_relations/remove_vertical_relation/', + { + 'vertical_relation_id': this.current_vertical_relation.id, + }, + (reponse) => { + show_info('Relacja zostaÅ‚a usuniÄ™ta.'); + this.current_vertical_relation = null; + this.loadFrame(); + $.prompt.close(); + }, + (request, errorType, errorMessage) => { + show_error(errorType + ' (' + errorMessage + ')'); + alert(gettext("UsuniÄ™cie relacji nie powiodÅ‚a siÄ™. Błąd: " + errorType + ' (' + errorMessage + ')')); + $.prompt.close(); + }) + } else { + alert(gettext("Rama źródÅ‚owa nie posiada jeszcze ustawionej relacji.")); + } + } + }, + mounted() { + + if(this.unifiedFrameId) { + this.loadFrame(); + } + + Split(['#semantics-frames-pane', '#semantics-schemata-pane'], { + sizes: [40, 60], + minSize: 10, + gutterSize: 4, + elementStyle: (dimension, size, gutterSize) => { + return { + 'flex-basis': 'calc(' + size + '% - ' + gutterSize + 'px)' + } + }, + }); + Split(['#source-unified-frame-pane', '#target-unified-frame-pane', '#examples'], { + sizes: [40, 40, 20], + direction: 'vertical', + gutterSize: 4, + minSize: 10, + }); + } +} + +</script> + +<template> + <div class="col h-100 px-0 pt-0 pb-0 overflow-auto" id="semantics-frames-pane"> + <div id="source-unified-frame-pane" class="col w-100 p-0 overflow-auto"> + <table v-if="!isReadOnlyForSuperLeksykograf()" class="table-button-menu sticky-top" cellspacing="1"> + <tr style="background-color: white;"> + <td id="change-main-relation" @click="save_main_vertical_relation()" style="padding: 10px 15px 10px 15px; color: #000000;">Dodaj relacjÄ™</td> + <td id="change-temporal-relation" @click="save_temporal_vertical_relation()" style="padding: 10px 15px 10px 15px; color: #000000;">Dodaj podrelacjÄ™</td> + <td id="remove-relation" @click="remove_vertical_relation()" style="padding: 10px 15px 10px 15px; color: #000000;">UsuÅ„ relacjÄ™</td> + <td id="available-relations" @click="load_vertical_relation()" style="padding: 10px 15px 10px 15px; color: #000000;">DostÄ™pne relacje</td> + </tr> + </table> + <spinner /> + <div align="center"> + <div align="left" style="display: table;"> + <div class="unifiedFrame mt-3" v-bind:data-frame_id="unified_frame.id" id="unified-frame-title" v-html="unified_frame_title"></div> + <table v-if="unified_frame.id" id="unified-frame" class="m-0 table-borderless border border-secondary text-dark frame active"> + <tbody> + <tr> + <template v-for="argument in unified_frame_arguments"> + <td + class="argument py-2 px-1 border-top border-left border-secondary role-column" + :class="getArgumentCSS(argument)" + @click="unifiedFrameArgumentSelected(argument)" + @mouseover="unifiedFrameArgumentHovered(argument)" + @mouseleave="unifiedFrameArgumentHovered(null)" + > + {{ argument.role_type }} + + <div + v-if="argument.role" + > + [{{ argument.role.str }}] + </div> + <div v-else> + <ul class="ul-role"> + <li v-for="proposed_role in argument.proposed_roles"> + {{ proposed_role.str }} + </li> + </ul> + </div> + </td> + </template> + + + </tr> + <tr> + <td class="preferences py-0 px-0 border-top border-left border-secondary role-column align-top" + v-for='argument in unified_frame_arguments' + :key='argument.id' + > + <ul class="ul-preference" v-if="argument.preferences.length > 0"> + <li v-for='preference in argument.preferences'> + <div + v-if="preference.url != null" + class="preference py-2 px-1 preference-bold" + > + <a class="synset-plwn" v-bind:href="preference.url" target="_blank">{{ preference.str }}</a> + </div> + <div v-else class="preference py-2 px-1 preference-bold">{{ preference.str }}</div> + </li> + </ul> + <ul class="ul-preference" v-if="unified_frame.status !== 'S'"> + <li v-for="preference in slowal_frames2selecional_preferencies_mapping[argument.id]"> + <span v-if="preference.url != null" class="preference py-2 px-1"> + <a class="synset-plwn" v-bind:href="preference.url" target="_blank">{{ preference.str }}</a> + </span> + <span v-else class="preference py-2 px-1">{{ preference.str }}</span> + <info-tooltip v-if="preference.info" :text="preference.info" /> + </li> + </ul> + </td> + </tr> + </tbody> + </table> + </div> + <div v-if="unified_frame.id" class="lu-table mt-3 mb-3"> + <table class="m-0 table-borderless border border-secondary text-dark"> + <td class="argument py-2 px-1 border-top border-left border-secondary"> + <template v-for='lexical_unit in lexical_units'> + {{ lexical_unit.str }}, + </template> + </td> + </table> + </div> + </div> + <div align="left"> + <div v-if="current_vertical_relation" class="lu-table mt-3 mb-3" style="padding-left: 50px;"> + <table class="m-0 table-borderless text-dark"> + <tr> + <td class="argument py-2 px-1 border-top border-left border-secondary" colspan="2"> + {{current_vertical_relation.main_vertical_relation.name}} + </td> + </tr> + </table> + </div> + </div> + </div> + <div id="target-unified-frame-pane" class="col w-100 p-0 overflow-auto"> + </div> + <div id="examples" class="col w-100 p-0 tab-pane overflow-auto"> + <examples-component v-if="examples" + :examples="examples" + :frame="active_slowal_frame" + :frame_arguments="selectedFrameArguments" + :frame_arguments_or_type="frame_arguments_or_type" + :lus="selectedLus" + :schemas="selectedSchemas" + :key="examples" + @example-selected="exampleSelected" + /> + </div> + </div> + <div class="col h-100 px-1 pt-0 pb-0 overflow-auto" style="padding-left: 0px!important; padding-right: 0px!important;" id="semantics-schemata-pane"> + <ul class="nav nav-pills nav-justified p-1" id="entryTabs" role="tablist"> + <li + v-for="tab in right_pane_tabs" + class="btn btn-sm btn-outline-dark nav-link mx-1" + :class="right_pane_tab === tab.id && 'active'" + @click="right_pane_tab = tab.id" + > + {{ tab.label }} + </li> + </ul> + <div v-if="right_pane_tab === 'hierarchy'" class="px-0 pt-0 pb-0 overflow-auto"> + <hierarchy-element :node="createHierarchyTree()" :spacing_elem_type="'none'"/> + </div> + <div :class="right_pane_tab !== 'notes' && 'd-none'" id="notes-component"></div> + </div> +</template> diff --git a/frontend/src/components/unification/vertical_relations/VerticalRelationElement.vue b/frontend/src/components/unification/vertical_relations/VerticalRelationElement.vue new file mode 100644 index 0000000..ee8d86e --- /dev/null +++ b/frontend/src/components/unification/vertical_relations/VerticalRelationElement.vue @@ -0,0 +1,188 @@ +<script> + +export default { + name: 'VerticalRelationElement', + props: { + node: { + type: Object, + required: true + }, + spacing: { + type: Number, + default: 0 + }, + spacing_elem_type: String + }, + data() { + return { + showHyponyms: false, + showHypyronyms: false + } + }, + computed: { + nodeMargin() { + return { + 'margin-left': `${this.spacing}px` + } + }, + hasHyponyms() { + const {hyponyms} = this.node + return hyponyms && hyponyms.length > 0 + }, + hasHypyronyms() { + const {hypyronyms} = this.node + return hypyronyms && hypyronyms.length > 0 + }, + toggleChildrenIcon() { + return this.showHypyronyms ? 'fas fa-angle-down' : 'fas fa-angle-right' + }, + getSpacingElem() { + if(this.spacing_elem_type === "std") { + return "│ ├→ │"; + } else if(this.spacing_elem_type === "top_corner") { + return "        ┌→ │ │"; + } else if(this.spacing_elem_type === "down_corner") { + return "│ │ └→"; + } else { + return ""; + } + }, + }, + methods: { + loadChildren (children) { + for (let i in children) { + const hypyronym = children[i]; + if (hypyronym.hasChildrenLoaded !== true) { + $.ajax({ + type: 'post', + url: '/' + lang + '/unifier/get_hierarchy_hyperonyms/' + hypyronym.unified_frame_id + "/", + dataType: 'json', + timeout: 60000, + success: function (response) { + hypyronym.hypyronyms = response.hyperonyms; + }.bind(this), + error: function (request, errorType, errorMessage) { + show_error(errorType + ' (' + errorMessage + ')'); + } + }); + $.ajax({ + type: 'post', + url: '/' + lang + '/unifier/get_hierarchy_hyponyms/' + hypyronym.unified_frame_id + "/", + dataType: 'json', + timeout: 60000, + success: function (response) { + hypyronym.hyponyms = response.hyponyms; + }.bind(this), + error: function (request, errorType, errorMessage) { + show_error(errorType + ' (' + errorMessage + ')'); + } + }); + } + } + }, toggleHypyronyms() { + this.loadChildren(this.node.hypyronyms); + this.showHypyronyms = !this.showHypyronyms + }, + toggleHyponyms() { + this.loadChildren(this.node.hyponyms); + this.showHyponyms = !this.showHyponyms + } + } +} + +</script> + + +<template> + <div :style="nodeMargin" style="height: 100%"> + <div v-if="hasHypyronyms" v-show="showHypyronyms"> + <hierarchy-element + v-for="(child, index) in node.hypyronyms" + :key="child.id" + :node="child" + :spacing_elem_type='index === 0 ? "top_corner" : "std"' + :spacing="spacing + 40" + /> + </div> + <div show class="d-flex justify-content-between mb-1"> + <div class="row"> + <div class="col" style="max-width: 10px" v-html="getSpacingElem"/> + <div class="col"> + <div class="row"> + <div class="col cursor-pointer" style="text-align: center; font-size: large"><span + v-if="hasHypyronyms" + :class="toggleChildrenIcon" + @click="toggleHypyronyms" + @keypress="toggleHypyronyms">▲</span></div> + </div> + <div class="row"> + <div class="col" align="left" style="display: table;"> + <div class="unifiedFrame" id="hierarchy-unified-frame-title" v-html="node.unified_frame.title"></div> + <table id="hierarchy-unified-frame" class="m-0 table-borderless border border-secondary text-dark frame active"> + <tbody> + <tr> + <template v-for="argument in node.unified_frame.arguments"> + <td + class="argument py-2 px-1 border-top border-left border-secondary role-column" + :class="argument.role ? argument.role.str + ' ' : ''" + > + {{ argument.role_type }} + + <div + v-if="argument.role" + > + [{{ argument.role.str }}] + </div> + <div v-else> + <ul class="ul-role"> + <li v-for="proposed_role in argument.proposed_roles"> + {{ proposed_role.str }} + </li> + </ul> + </div> + </td> + </template> + </tr> + <tr> + <td hidden class="preferences py-0 px-0 border-top border-left border-secondary role-column align-top" + v-for='argument in node.unified_frame.arguments' + :key='argument.id' + > + <ul class="ul-preference" v-if="argument.preferences.length > 0"> + <li v-for='preference in argument.preferences'> + <div + v-if="preference.url != null" + class="preference py-2 px-1 preference-bold" + > + <a class="synset-plwn" v-bind:href="preference.url" target="_blank">{{ preference.str }}</a> + </div> + <div v-else class="preference py-2 px-1 preference-bold">{{ preference.str }}</div> + </li> + </ul> + </td> + </tr> + </tbody> + </table> + </div> + </div> + <div class="row align-content-center"> + <div class="col cursor-pointer" style="text-align: center; font-size: large"><span + v-if="hasHyponyms" + :class="toggleChildrenIcon" + @click="toggleHyponyms" + @keypress="toggleHyponyms">▼</span></div> + </div> + </div> + </div> + </div> + <div v-if="hasHyponyms" v-show="showHyponyms"> + <vertical-relation-element + v-for="(child, index) in node.hyponyms" + :key="child.id" + :node="child" + :spacing_elem_type='index === node.hyponyms.length - 1 ? "down_corner" : "std"' + :spacing="spacing + 40" + /> + </div> + </div> +</template> diff --git a/frontend/src/components/unification/vertical_relations/VerticalRelationPreview.vue b/frontend/src/components/unification/vertical_relations/VerticalRelationPreview.vue new file mode 100644 index 0000000..a88db17 --- /dev/null +++ b/frontend/src/components/unification/vertical_relations/VerticalRelationPreview.vue @@ -0,0 +1,64 @@ +<script> +import UnificationFramesList from "../Unification/UnificationFramesList.vue"; + +export default { + props: { + initialUnifiedFrameId: Number, + initialLexicalUnitId: Number, + forceRefresh: Number, + hierarchyEditComponent: Object, + }, + data () { + return { + unifiedFrameId: this.initialUnifiedFrameId + }; + }, + components: {UnificationFramesList}, + emits: ['changeFrame', 'refreshEntriesList'], + methods: { + unifiedFrameSelected (unifiedFrameId) { + this.$emit('changeFrame', unifiedFrameId); + }, + refreshEntriesList() { + this.$emit('refreshEntriesList') + }, + }, + mounted () { + Split(['#frame-preview-left-pane', '#frame-preview-right-pane'], { + sizes: [60, 40], + minSize: 20, + gutterSize: 4, + elementStyle: (dimension, size, gutterSize) => { + return { + 'flex-basis': 'calc(' + size + '% - ' + gutterSize + 'px)' + } + }, + }); + } +}; +</script> + +<template> + <div class="row h-100 overflow-hidden"> + <div :key="unifiedFrameId" class="col h-100 pr-0 pt-0 pb-0 overflow-auto" id="frame-preview-left-pane"> + <component v-bind:is="hierarchyEditComponent" + v-if="unifiedFrameId !== -1" + :readOnly="true" + :unifiedFrameId="unifiedFrameId" + :forceRefresh="forceRefresh" + @refresh-entries-list="refreshEntriesList" + /> + <div v-else class="h-100"> + Brak ramy do wyÅ›wietlenia + </div> + </div> + <div class="col h-100 pl-1 pt-0 pb-0 overflow-auto" id="frame-preview-right-pane"> +<!-- <unification-switchable-list--> +<!-- @unified-frame-selected="unifiedFrameSelected"--> +<!-- />--> + <unification-frames-list + @unified-frame-selected="unifiedFrameSelected" + /> + </div> + </div> +</template> diff --git a/frontend/src/components/unification/vertical_relations/VerticalRelationRightPane.vue b/frontend/src/components/unification/vertical_relations/VerticalRelationRightPane.vue new file mode 100644 index 0000000..b240ac4 --- /dev/null +++ b/frontend/src/components/unification/vertical_relations/VerticalRelationRightPane.vue @@ -0,0 +1,66 @@ +<script> +import VerticalRelationEdit from './VerticalRelationEdit.vue'; + +export default { + components: {VerticalRelationEdit}, + props: { + entryId: Number, + lexicalUnitId: Number, + initialUnifiedFrameId: Number, + }, + emits: ['refreshEntriesList'], + data() { + return this.getInitialData(); + }, + methods: { + getInitialData() { + return { + key: this.lexicalUnitId, + entryIdLocal: this.entryId, + unifiedFrameId: this.initialUnifiedFrameId, + previewedUnifiedFrameId: -1 + }; + }, + goToDisplay() { + this.unifiedFrameId = null; + }, + refresh() { + this.key = null; + setTimeout(() => { + this.key = this.lexicalUnitId; + }, 0); + }, + swapFrames(previewedUnifiedFrameId) { + this.previewedUnifiedFrameId = this.unifiedFrameId; + this.unifiedFrameId = previewedUnifiedFrameId; + this.refresh(); + }, + refreshEntriesList() { + this.$emit('refreshEntriesList'); + } + }, + watch: { + lexicalUnitId() { + Object.assign(this, this.getInitialData()); + }, + initialUnifiedFrameId() { + Object.assign(this, this.getInitialData()); + } + }, +}; +</script> + +<template> + <div v-if="key || unifiedFrameId" :key="(key, entryIdLocal, unifiedFrameId)" class="row h-100 m-0 p-0 overflow-auto" id="semantics-top-pane"> + <vertical-relation-edit + ref="hierarchyEdit" + v-if="unifiedFrameId" + :key="unifiedFrameId" + :unifiedFrameId="unifiedFrameId" + :previewedUnifiedFrameId="previewedUnifiedFrameId" + @go-to-display="goToDisplay" + @swap-frames="swapFrames" + @refresh-entries-list="refreshEntriesList" + /> + </div> +</template> diff --git a/frontend/src/main.js b/frontend/src/main.js index 6162fa0..4771d25 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -7,6 +7,7 @@ import Entries from "./components/unification/Entries/Entries.vue"; import Unification from "./components/unification/Unification/Unification.vue"; import Hierarchy from "./components/unification/hierarchy/Hierarchy.vue"; import FreeLu from "./components/unification/free_lu/FreeLu.vue"; +import VerticalRelation from "./components/unification/vertical_relations/VerticalRelation.vue"; const router = createRouter({ history: createWebHistory(), @@ -15,6 +16,7 @@ const router = createRouter({ { path: '/:lang/entries/unification/', component: Unification }, { path: '/:lang/entries/hierarchy/', component: Hierarchy }, { path: '/:lang/entries/lu_free/', component: FreeLu }, + { path: '/:lang/entries/vertical-relations/', component: VerticalRelation }, { path: '/:pathMatch(.*)*', component: null, name: '404' }, ] }); diff --git a/shellvalier/settings.py b/shellvalier/settings.py index 0fff663..96df497 100644 --- a/shellvalier/settings.py +++ b/shellvalier/settings.py @@ -68,6 +68,7 @@ INSTALLED_APPS = [ 'unifier.apps.UnifierConfig', 'financial_settlement.apps.FinStatementConfig', 'freelus.apps.FreeLusConfig', + 'vertical_relations.apps.VerticalRelationsConfig', ] CRISPY_TEMPLATE_PACK = 'bootstrap4' diff --git a/shellvalier/urls.py b/shellvalier/urls.py index 2d79020..8ce3c85 100644 --- a/shellvalier/urls.py +++ b/shellvalier/urls.py @@ -20,6 +20,7 @@ urlpatterns = i18n_patterns( path('unifier/', include('unifier.urls')), path('financial_settlement/', include('financial_settlement.urls')), path('freelus/', include('freelus.urls')), + path('vertical_relations/', include('vertical_relations.urls')), path('', dash, name='dash'), # uncomment to leave default (Polish) urls unchanged #prefix_default_language=False, diff --git a/syntax/management/commands/import_tei.py b/syntax/management/commands/import_tei.py index e98c014..f1afea6 100644 --- a/syntax/management/commands/import_tei.py +++ b/syntax/management/commands/import_tei.py @@ -26,6 +26,7 @@ from syntax.models_phrase import ( LemmaOperator, LemmaCooccur, ModificationType, ) +from vertical_relations.models import MainVerticalRelation, TemporalVerticalRelation class Command(BaseCommand): @@ -357,3 +358,21 @@ def import_modification_types(): for pri, name in modtypes: modtype = ModificationType(name=name, priority=pri) modtype.save() + + +def import_vertical_relations(): + main_relations = [(1, u'cause(s)'), (2, u'conditioned'), (3, u'presuppose(s) (pressuposition)'), + (4, u'exclude(s)'), (5, u'antonimic'), (6, u'implicate(s)')] + + for pri, name in main_relations: + relation = MainVerticalRelation(name=name, priority=pri) + relation.save() + + temporal_relations = [(1, u'before'), (2, u'meets'), (3, u'overlaps'), + (4, u'begins'), (5, u'during'), (6, u'ends'), (7, u'equals'), + (8, u'after'), (9, u'met'), (10, u'overlapped'), (11, u'begun'), + (12, u'contains'), (13, u'ended')] + + for pri, name in temporal_relations: + relation = TemporalVerticalRelation(name=name, priority=pri) + relation.save() diff --git a/unifier/views.py b/unifier/views.py index e11c3b0..f7b4ada 100644 --- a/unifier/views.py +++ b/unifier/views.py @@ -27,6 +27,7 @@ from syntax.models import Schema from unifier.models import UnifiedFrameArgument, UnifiedRelationalSelectionalPreference, UnifiedFrame, \ UnifiedFrame2SlowalFrameMapping, UnifiedFrameArgumentSlowalFrameMapping, HierarchyModel from users.models import Assignment +from vertical_relations.models import UnifiedFrameVerticalRelation from . import choices from .apps import synset_hierarchy_dict, SynsetHierarchy from .choices import UnifiedFrameStatus @@ -485,12 +486,28 @@ def get_unified_frame_json(unified_frame, request): unified_frame_dict = unified_frame_2_dict(unified_frame) - return {'unified_frame_id': unified_frame.id, 'unified_frame': unified_frame_dict, 'subentries': subentries, + vertical_relations = UnifiedFrameVerticalRelation.objects.filter(source_unified_frame=unified_frame) + vertical_relations = vertical_relations.prefetch_related("target_unified_frame") \ + .prefetch_related("target_unified_frame").prefetch_related("main_vertical_relation")\ + .prefetch_related("temporal_vertical_relation")\ + .prefetch_related("iterated_target_unified_frame").prefetch_related("iterated_temporal_vertical_relation") + + return {'vertical_relations': vertical_relations_2_dict(vertical_relations), 'unified_frame_id': unified_frame.id, 'unified_frame': unified_frame_dict, 'subentries': subentries, 'frames': slowal_frames_dict, 'alternations': alternations, 'realisation_phrases': realisation_phrases, 'realisation_descriptions': realisation_descriptions, 'examples': examples, 'last_visited': request.session['last_visited']} +def vertical_relations_2_dict(vertical_relations): + return [ + { + 'str': str(vertical_relation), + 'id': vertical_relation.id, + 'source_unified_frame_id': vertical_relation.source_unified_frame.id, + } for vertical_relation in sorted(vertical_relations.all(), key=lambda x: x.id) + ] + + @ajax_required @login_required def get_unified_frame(request): diff --git a/vertical_relations/__init__.py b/vertical_relations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vertical_relations/apps.py b/vertical_relations/apps.py new file mode 100644 index 0000000..62debb1 --- /dev/null +++ b/vertical_relations/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class VerticalRelationsConfig(AppConfig): + name = 'vertical_relations' + diff --git a/vertical_relations/management/commands/__init__.py b/vertical_relations/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vertical_relations/management/commands/db_import_relation_entries.py b/vertical_relations/management/commands/db_import_relation_entries.py new file mode 100644 index 0000000..2dcb4a3 --- /dev/null +++ b/vertical_relations/management/commands/db_import_relation_entries.py @@ -0,0 +1,15 @@ +from django.core.management.base import BaseCommand + +from syntax.management.commands.import_tei import import_vertical_relations + + +class Command(BaseCommand): + args = '' + help = 'Import relation types to database.' + + def handle(self, **options): + import_vertical_relations_cmd() + + +def import_vertical_relations_cmd(): + import_vertical_relations() diff --git a/vertical_relations/models.py b/vertical_relations/models.py new file mode 100644 index 0000000..c4eea01 --- /dev/null +++ b/vertical_relations/models.py @@ -0,0 +1,63 @@ +from django.db import models + +from unifier.models import UnifiedFrame, UnifiedFrameArgument + + +class MainVerticalRelation(models.Model): + name = models.CharField(max_length=100) + priority = models.PositiveIntegerField() + + class Meta: + ordering = ['priority'] + + def __str__(self): + return self.name + + +class TemporalVerticalRelation(models.Model): + name = models.CharField(max_length=100) + priority = models.PositiveIntegerField() + + class Meta: + ordering = ['priority'] + + def __str__(self): + return self.name + + +class UnifiedFrameArgument2UnifiedFrameArgumentMapping(object): + pass + + +class UnifiedFrameVerticalRelation(models.Model): + source_unified_frame = models.ForeignKey(UnifiedFrame, related_name='source_vertical_relation', + default=None, blank=True, null=True, on_delete=models.PROTECT) + target_unified_frame = models.ForeignKey(UnifiedFrame, related_name='target_vertical_relation', + default=None, blank=True, null=True, on_delete=models.PROTECT) + main_vertical_relation = models.ForeignKey(MainVerticalRelation, related_name='unified_frame_vertical_relation', + default=None, blank=True, null=True, on_delete=models.PROTECT) + temporal_vertical_relation = models.ManyToManyField(related_name='unified_frame_vertical_relation', + to=TemporalVerticalRelation) + is_negated = models.BooleanField(default=False) + + iterated_target_unified_frame = models.ForeignKey(UnifiedFrame, related_name='iterated_target_vertical_relation', + default=None, blank=True, null=True, on_delete=models.PROTECT) + iterated_temporal_vertical_relation = models.ManyToManyField(related_name='iterated_unified_frame_vertical_relation', + to=TemporalVerticalRelation) + + def __str__(self): + iter_str = ('(iter:' + self.iterated_target_unified_frame.ttile + ' - ' + + (','.join(self.iterated_temporal_vertical_relation.all()) if self.iterated_temporal_vertical_relation else '') + ')') if self.iterated_target_unified_frame else '' + + return self.source_unified_frame.title + ':' + (self.target_unified_frame.title if self.target_unified_frame else '') + \ + ' - ' + ('[neg]' if self.is_negated else '') + self.main_vertical_relation.name + ', ' + \ + (','.join(self.temporal_vertical_relation.all()) if self.is_negated else '') + iter_str + + +class UnifiedFrameArgument2UnifiedFrameArgumentMapping(models.Model): + source_unified_agrument = models.ForeignKey(UnifiedFrameArgument, related_name='source_unified_agrument_mapping', + on_delete=models.PROTECT) + target_unified_agrument = models.ForeignKey(UnifiedFrameArgument, related_name='target_unified_agrument_mapping', on_delete=models.PROTECT) + unified_frame_vertical_relation = models.ForeignKey(UnifiedFrameVerticalRelation, + related_name='unified_frame_arguments_mapping', on_delete=models.PROTECT) + diff --git a/vertical_relations/urls.py b/vertical_relations/urls.py new file mode 100644 index 0000000..0c597d4 --- /dev/null +++ b/vertical_relations/urls.py @@ -0,0 +1,15 @@ +from django.urls import path + +from . import views + +app_name = 'vertical_relations' + +urlpatterns = [ + path('save_main_vertical_relation/', views.save_main_vertical_relation, name='save_main_vertical_relation'), + path('ajax_main_vertical_relations/', views.ajax_main_vertical_relations, name='ajax_main_vertical_relations'), + path('ajax_temporal_vertical_relations/', views.ajax_temporal_vertical_relations, name='ajax_temporal_vertical_relations'), + path('load_vertical_relation/', views.load_vertical_relation, name='load_vertical_relation'), + path('save_temporal_vertical_relation/', views.save_temporal_vertical_relation, name='save_temporal_vertical_relation'), + path('remove_vertical_relation/', views.remove_vertical_relation, name='remove_vertical_relation'), + +] diff --git a/vertical_relations/views.py b/vertical_relations/views.py new file mode 100644 index 0000000..ed17d73 --- /dev/null +++ b/vertical_relations/views.py @@ -0,0 +1,128 @@ +import json + +from django.db import transaction +from django.http import JsonResponse + +from common.decorators import ajax_required, ajax +from unifier.views import unified_frame_2_dict +from vertical_relations.models import UnifiedFrameVerticalRelation, MainVerticalRelation, TemporalVerticalRelation + + +@ajax(method='get', encode_result=True) +def ajax_main_vertical_relations(request): + main_vertical_relations = [] + for main_vertical_relation in MainVerticalRelation.objects.order_by('priority'): + main_vertical_relations.append({"id": main_vertical_relation.id, "name": main_vertical_relation.name}) + + context = { + 'main_vertical_relations': main_vertical_relations, + } + + return context + + +@ajax(method='get', encode_result=True) +def ajax_temporal_vertical_relations(request): + temporal_vertical_relations = [] + for temporal_vertical_relation in TemporalVerticalRelation.objects.order_by('priority'): + temporal_vertical_relations.append({"id": temporal_vertical_relation.id, "name": temporal_vertical_relation.name}) + + context = { + 'temporal_vertical_relations': temporal_vertical_relations, + } + + return context + + +@ajax_required +@transaction.atomic +def save_main_vertical_relation(request): + if request.method == 'POST': + source_unified_frame_id = request.POST['source_unified_frame_id'] + target_unified_frame_id = request.POST['target_unified_frame_id'] + main_vertical_relation_id = request.POST['main_vertical_relation_id'] + + if target_unified_frame_id: + relation, _ = UnifiedFrameVerticalRelation.objects.get_or_create(source_unified_frame_id=source_unified_frame_id, + target_unified_frame_id=target_unified_frame_id) + else: + relation, _ = UnifiedFrameVerticalRelation.objects.get_or_create(source_unified_frame_id=source_unified_frame_id, + target_unified_frame_id=None) + + main_vertical_relation = MainVerticalRelation.objects.get(id=main_vertical_relation_id) + relation.main_vertical_relation = main_vertical_relation + relation.save() + + return JsonResponse({'vertical_relation': get_vertical_relation(relation.id)}) + + return JsonResponse({}) + + +@ajax_required +@transaction.atomic +def save_temporal_vertical_relation(request): + if request.method == 'POST': + source_unified_frame_id = request.POST['source_unified_frame_id'] + target_unified_frame_id = request.POST['target_unified_frame_id'] + temporal_vertical_relation_ids = json.loads(request.POST['temporal_vertical_relation_ids']) + + relation = UnifiedFrameVerticalRelation.objects.get(source_unified_frame_id=source_unified_frame_id, + target_unified_frame_id=target_unified_frame_id if target_unified_frame_id else None) + + for temporal_vertical_relation_id in temporal_vertical_relation_ids: + temporal_relation = TemporalVerticalRelation.objects.get(id=temporal_vertical_relation_id) + relation.temporal_vertical_relation.add(temporal_relation) + + relation.save() + + return JsonResponse({'vertical_relation': get_vertical_relation(relation.id)}) + + return JsonResponse({}) + + +@ajax_required +@transaction.atomic +def load_vertical_relation(request): + if request.method == 'POST': + vertical_relation_id = request.POST['vertical_relation_id'] + return JsonResponse({'vertical_relation': get_vertical_relation(vertical_relation_id)}) + + return JsonResponse({}) + + +def relation2dict(vertical_relation): + return { + 'id': vertical_relation.id, + 'is_negated': vertical_relation.is_negated, + 'target_unified_frame': unified_frame_2_dict(vertical_relation.target_unified_frame) if vertical_relation.target_unified_frame else None, + 'main_vertical_relation': { + 'id': vertical_relation.main_vertical_relation.id, + 'name': vertical_relation.main_vertical_relation.name, + }, + 'temporal_vertical_relation': [{ + 'id': temporal_relation.id, + 'name': temporal_relation.name, + } for temporal_relation in vertical_relation.temporal_vertical_relation.all()] if vertical_relation.temporal_vertical_relation else None, + 'iterated_target_unified_frame': unified_frame_2_dict(vertical_relation.iterated_target_unified_frame) if vertical_relation.iterated_target_unified_frame else None, + 'iterated_temporal_vertical_relation': [{ + 'id': temporal_relation.id, + 'name': temporal_relation.name, + } for temporal_relation in vertical_relation.iterated_temporal_vertical_relation.all()] if vertical_relation.iterated_temporal_vertical_relation else None, + } + + +def get_vertical_relation(vertical_relation_id): + vertical_relation = UnifiedFrameVerticalRelation.objects.get(id=vertical_relation_id) + + return relation2dict(vertical_relation) + + +@ajax_required +@transaction.atomic +def remove_vertical_relation(request): + if request.method == 'POST': + vertical_relation_id = request.POST['vertical_relation_id'] + UnifiedFrameVerticalRelation.objects.get(id=vertical_relation_id).delete() + return JsonResponse({}) + + return JsonResponse({}) -- GitLab