From f3306f702794200c325c3e5ad15c58b60e376248 Mon Sep 17 00:00:00 2001
From: dcz <dcz@ipipan.waw.pl>
Date: Thu, 21 Sep 2023 17:01:46 +0200
Subject: [PATCH] role -> typical ... Check a synset hierarchy at selectional
 preference add.

---
 .../Unification/LexicalUnitEdit.vue           | 13 +++-
 .../Unification/SelectionalPreference.js      | 21 ++++-
 unifier/apps.py                               | 48 ++++++++++++
 unifier/views.py                              | 76 ++++++++++++++++++-
 4 files changed, 152 insertions(+), 6 deletions(-)

diff --git a/frontend/src/components/unification/Unification/LexicalUnitEdit.vue b/frontend/src/components/unification/Unification/LexicalUnitEdit.vue
index 60ed43d..fc30aba 100644
--- a/frontend/src/components/unification/Unification/LexicalUnitEdit.vue
+++ b/frontend/src/components/unification/Unification/LexicalUnitEdit.vue
@@ -279,6 +279,15 @@ Object.assign(LexicalUnitEdit, {
       }
       $.prompt(change_title_popup);
     },
+    roleStrMapping(roleName) {
+      if(roleName === 'role') {
+        return 'required';
+      } else if(roleName === 'modifier') {
+        return 'typical';
+      } else {
+        return roleName;
+      }
+    },
     changeRole() {
       if (!this.active_unified_frame_argument) {
         alert(gettext("Zaznacz argument, dla którego chcesz wybrać rolę."));
@@ -305,7 +314,7 @@ Object.assign(LexicalUnitEdit, {
           }).join("");
           // const roleTypeHTML = ['required', 'typical'].map(type => {
           const roleTypeHTML = ['role', 'modifier'].map(type => {
-            return `<label><input type="radio" name="role_type" value="${type}" /> ${type}</label><br />`;
+            return `<label><input type="radio" name="role_type" value="${type}" /> ${this.roleStrMapping(type)}</label><br />`;
           }).join("");
           return '<div class="row">' +
               '<div class="column"><div class="role_select_header">Type</div>' + roleTypeHTML + '</div>' +
@@ -1125,7 +1134,7 @@ export default LexicalUnitEdit;
                     @mouseover="unifiedFrameArgumentHovered(argument)"
                     @mouseleave="unifiedFrameArgumentHovered(null)"
                 >
-                <span style="font-size: 0.8rem;">{{ argument.role_type }}</span>
+                <span style="font-size: 0.8rem;">{{ roleStrMapping(argument.role_type) }}</span>
                 <div
                   v-if="argument.role"
                 >
diff --git a/frontend/src/components/unification/Unification/SelectionalPreference.js b/frontend/src/components/unification/Unification/SelectionalPreference.js
index abde3b5..46d1485 100644
--- a/frontend/src/components/unification/Unification/SelectionalPreference.js
+++ b/frontend/src/components/unification/Unification/SelectionalPreference.js
@@ -318,7 +318,26 @@ export default class SelectionalPreference {
       data: data,
       timeout: 60000,
       success: function (response) {
-        show_info('Preferencja zosała zapisana');
+        const succ = response.succ;
+        if(succ) {
+          show_info('Preferencja zosała zapisana');
+        } else {
+          const conflict_hyponym = response.conflict_hyponym;
+          const conflict_hyperonym = response.conflict_hyperonym;
+          let typeStr = 'hiponimii';
+          if(conflict_hyperonym != null){
+            typeStr = 'hieronimii';
+          }
+          let errorStr = conflict_hyponym;
+          if(conflict_hyperonym != null){
+            errorStr = conflict_hyperonym;
+          }
+          alert('Wybrana preferencja selekcyjna nie mogła zostać zapisana ponieważ wystąpił konflikt w relacji ' +
+              typeStr +
+              ' z istniejÄ…cÄ… preferencjÄ… selekcyjnÄ…: ' +
+              errorStr)
+          show_info('Preferencja nie zosała zapisana. Wystąpił konflikt w hierarchii ontologicznej.');
+        }
       },
       error: function (request, errorType, errorMessage) {
         show_error(errorType + ' (' + errorMessage + ')');
diff --git a/unifier/apps.py b/unifier/apps.py
index dd387d2..e18d141 100644
--- a/unifier/apps.py
+++ b/unifier/apps.py
@@ -1,5 +1,53 @@
 from django.apps import AppConfig
 
 
+class SynsetHierarchy:
+    id = -1
+    hyponyms = []
+    hyperonyms = []
+
+    def __init__(self, id):
+        self.id = id
+        self.hyponyms = []
+        self.hyperonyms = []
+
+
+synset_hierarchy_dict = {}
+
+
 class UnifierConfig(AppConfig):
     name = 'unifier'
+
+    def ready(self):
+        print("XXXXXXXXXXXXX")
+        from meanings.models import Synset
+        from django.db.models import Prefetch
+        i = 0
+        objs = Synset.objects
+        objs = objs.prefetch_related(
+            Prefetch(
+                "hypernyms",
+
+            )
+        )
+        for synset in objs.all():
+            print("i: "+str(i))
+            i = i + 1
+            curr = None
+            if synset.id not in synset_hierarchy_dict:
+                curr = SynsetHierarchy(id=synset.id)
+                synset_hierarchy_dict[synset.id] = curr
+            else:
+                curr = synset_hierarchy_dict[synset.id]
+
+            for hypernym in synset.hypernyms.all():
+                curr_hypernym = None
+                if hypernym.id not in synset_hierarchy_dict:
+                    curr_hypernym = SynsetHierarchy(id=hypernym.id)
+                    synset_hierarchy_dict[hypernym.id] = curr_hypernym
+                else:
+                    curr_hypernym = synset_hierarchy_dict[hypernym.id]
+
+                curr.hyperonyms.append(curr_hypernym)
+                curr_hypernym.hyponyms.append(curr)
+
diff --git a/unifier/views.py b/unifier/views.py
index e7c63b8..99c2add 100644
--- a/unifier/views.py
+++ b/unifier/views.py
@@ -33,9 +33,20 @@ from django.views.decorators.csrf import csrf_exempt
 
 import urllib3
 
+from .apps import synset_hierarchy_dict, SynsetHierarchy
 from .choices import UnifiedFrameStatus
 
 
+def get_predefined_preference_synsets(predefined, list_to_fill):
+    for pp in predefined:
+        if pp.members:
+            synsets_all = pp.members.synsets.all()
+            list_to_fill.extend(list(map(lambda s: s.id, synsets_all)))
+            generals_all = pp.members.generals.all()
+            if generals_all:
+                get_predefined_preference_synsets(generals_all, list_to_fill)
+
+
 @ajax_required
 @transaction.atomic
 def save_synset_preference(request):
@@ -45,10 +56,30 @@ def save_synset_preference(request):
         synset_preference_id = request.POST['synset_preference_id']
 
         unified_frame_argument = UnifiedFrameArgument.objects.get(unified_frame_id=int(frame_id), id=int(complement_id))
-        unified_frame_argument.synsets.add(int(synset_preference_id))
-        unified_frame_argument.save()
 
-        update_argument_preferences_count(unified_frame_argument)
+        synset_ids_map = map(lambda s: s.id, unified_frame_argument.synsets.all())
+
+        predefined_synset_ids = []
+        get_predefined_preference_synsets(unified_frame_argument.predefined.all(), predefined_synset_ids)
+
+        synset_ids_list = list(synset_ids_map)
+        synset_ids_list.extend(predefined_synset_ids)
+
+        conflict_hyponym, conflict_hyperonym = synset_hierarchy_constraint_check(int(synset_preference_id), set(synset_ids_list))
+
+        if conflict_hyponym is None and conflict_hyperonym is None:
+            unified_frame_argument.synsets.add(int(synset_preference_id))
+            unified_frame_argument.save()
+            update_argument_preferences_count(unified_frame_argument)
+            return JsonResponse({"succ": True})
+        else:
+            conflict_hyponym_lu = LexicalUnit.objects.filter(synset_id=conflict_hyponym) if conflict_hyponym is not None else None
+            conflict_hyperonym_lu = LexicalUnit.objects.filter(synset_id=conflict_hyperonym) if conflict_hyperonym is not None else None
+
+            conflict_hyponym_lu_str = ','.join(map(lambda s: str(s), conflict_hyponym_lu.all())) if conflict_hyponym is not None else None
+            conflict_hyperonym_lu_str = ','.join(map(lambda s: str(s), conflict_hyperonym_lu.all())) if conflict_hyperonym is not None else None
+
+            return JsonResponse({"succ": False, "conflict_hyponym": conflict_hyponym_lu_str, "conflict_hyperonym": conflict_hyperonym_lu_str})
 
     return JsonResponse({})
 
@@ -889,3 +920,42 @@ def attach_lu_to_unified_frame(request):
 
         return JsonResponse({})
     return JsonResponse({})
+
+
+def synset_hierarchy_constraint_check(lu_id, pref_lu_ids):
+
+    conflict_hyponym = None
+    if lu_id in synset_hierarchy_dict:
+        synset_hierarchy = synset_hierarchy_dict[lu_id]
+        conflict_hyponym = hierarchy_constraint_check_hypo(synset_hierarchy, lu_id, pref_lu_ids)
+
+    conflict_hyperonym = None
+    if lu_id in synset_hierarchy_dict:
+        synset_hierarchy = synset_hierarchy_dict[lu_id]
+        conflict_hyperonym = hierarchy_constraint_check_hyperonyms(synset_hierarchy, lu_id, pref_lu_ids)
+
+    return conflict_hyponym, conflict_hyperonym
+
+
+def hierarchy_constraint_check_hyperonyms(synset_hierarchy: SynsetHierarchy, lu_id: int, pref_lu_ids: []):
+    for synset in synset_hierarchy.hyperonyms:
+        if synset.id in pref_lu_ids:
+            return synset.id
+    for synset in synset_hierarchy.hyperonyms:
+        ret = hierarchy_constraint_check_hyperonyms(synset_hierarchy_dict[synset.id], lu_id, pref_lu_ids)
+        if ret is not None:
+            return ret
+
+    return None
+
+
+def hierarchy_constraint_check_hypo(synset_hierarchy: SynsetHierarchy, lu_id: int, pref_lu_ids: []):
+    for synset in synset_hierarchy.hyponyms:
+        if synset.id in pref_lu_ids:
+            return synset.id
+    for synset in synset_hierarchy.hyponyms:
+        ret = hierarchy_constraint_check_hypo(synset_hierarchy_dict[synset.id], lu_id, pref_lu_ids)
+        if ret is not None:
+            return ret
+
+    return None
-- 
GitLab