Skip to content

Commit 57ca817

Browse files
authored
Merge pull request #539 from digitalfabrik/show_existing_words
Inform users of word duplication in document form
2 parents 0ce465b + f18e2f1 commit 57ca817

11 files changed

Lines changed: 205 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ UNRELEASED
44
* [ [#353](https://github.com/digitalfabrik/lunes-cms/issues/353) ] Filter feedback by creator of related objects
55
* [ [#468](https://github.com/digitalfabrik/lunes-cms/issues/468) ] Excel list out of existing vocabulary in cms
66
* [ [#534](https://github.com/digitalfabrik/lunes-cms/issues/534) ] Adjust form appearance for small screens
7+
* [ [#470](https://github.com/digitalfabrik/lunes-cms/issues/470) ] Inform users of word duplication in document form
78

89

910
2024.5.1

lunes_cms/api/utils.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
from django.core.exceptions import PermissionDenied
66
from django.db.models import Count, Q
7+
from django.http import JsonResponse
8+
from django.utils.translation import ugettext_lazy as _
9+
710
from rest_framework import routers
811

9-
from ..cms.models import Discipline, GroupAPIKey
10-
from ..cms.utils import get_child_count
12+
from ..cms.models import Discipline, GroupAPIKey, Document
13+
from ..cms.utils import get_child_count, document_to_string
1114

1215

1316
class OptionalSlashRouter(routers.DefaultRouter):
@@ -151,3 +154,38 @@ def check_group_object_permissions(request, group_id):
151154
api_key_object = GroupAPIKey.get_from_token(key)
152155
if api_key_object.group_id != int(group_id):
153156
raise PermissionDenied()
157+
158+
159+
def find_duplicates_for_word(request, word):
160+
"""
161+
Function to find existing words that match the input in the "word" field of document
162+
163+
:param request: current request
164+
:type request: HttpRequest
165+
166+
:param group_id: input in the "word"field of document
167+
:type group_id: str
168+
169+
:return: Whether any duplicate was found, and some details of the word if found
170+
:rtype: JsonResponse
171+
172+
"""
173+
174+
if duplicate := Document.objects.filter(word=word).first():
175+
training_sets_description = _("This word is assigned to no training set.")
176+
if training_sets := duplicate.training_sets.all():
177+
training_sets_description = ", ".join(
178+
str(training_set) for training_set in training_sets
179+
)
180+
181+
result = {
182+
"message": _("This word is already registered in the system."),
183+
"word": document_to_string(duplicate) + " (" + duplicate.word_type + ")",
184+
"definition": _("Definition: ") + duplicate.definition
185+
if duplicate.definition
186+
else _("Definition: ") + _("No definition is provided for this word."),
187+
"training_sets": _("Training sets: ") + training_sets_description,
188+
}
189+
190+
return JsonResponse(result)
191+
return JsonResponse({"message": _("This word is not yet registered in the system")})

lunes_cms/api/v1/urls.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from django.urls import include, path
55
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
66

7-
from ..utils import OptionalSlashRouter
7+
from ..utils import OptionalSlashRouter, find_duplicates_for_word
88
from . import views
99

1010
#: The namespace for this URL config (see :attr:`django.urls.ResolverMatch.app_name`)
@@ -59,4 +59,9 @@
5959
),
6060
name="swagger-ui",
6161
),
62+
path(
63+
"search_duplicate/<word>",
64+
find_duplicates_for_word,
65+
name="search_duplicate",
66+
),
6267
]

lunes_cms/cms/fixtures/test_data.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6891,6 +6891,7 @@
68916891
"word_type": "Nomen",
68926892
"word": "Ei",
68936893
"singular_article": 3,
6894+
"definition": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua",
68946895
"audio": "audio/ei.mp3",
68956896
"creation_date": "2021-08-09T13:52:38.131Z",
68966897
"created_by": null,
@@ -13411,6 +13412,20 @@
1341113412
"creator_is_admin": true
1341213413
}
1341313414
},
13415+
{
13416+
"model": "cms.document",
13417+
"pk": 2332,
13418+
"fields": {
13419+
"word_type": "Verb",
13420+
"word": "implementieren",
13421+
"singular_article": 0,
13422+
"definition": "in ein bestehendes Computersystem einsetzen, einbauen und so ein funktionsfähiges Programm erstellen",
13423+
"example_sentence": "eine neue Software implementieren",
13424+
"creation_date": "2021-04-13T15:58:10.468Z",
13425+
"created_by": null,
13426+
"creator_is_admin": true
13427+
}
13428+
},
1341413429
{
1341513430
"model": "cms.alternativeword",
1341613431
"pk": 1,

lunes_cms/locale/de/LC_MESSAGES/django.po

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ msgid ""
77
msgstr ""
88
"Project-Id-Version: PACKAGE VERSION\n"
99
"Report-Msgid-Bugs-To: \n"
10-
"POT-Creation-Date: 2024-06-11 15:29+0000\n"
10+
"POT-Creation-Date: 2024-06-19 14:40+0000\n"
1111
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1212
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -21,6 +21,30 @@ msgstr ""
2121
msgid "API"
2222
msgstr "API"
2323

24+
#: api/utils.py:175
25+
msgid "This word is assigned to no training set."
26+
msgstr "Das word ist zu keinem Modul zugeordnet."
27+
28+
#: api/utils.py:182
29+
msgid "This word is already registered in the system."
30+
msgstr "Das Wort ist schon im System hinterlegt."
31+
32+
#: api/utils.py:184 api/utils.py:186
33+
msgid "Definition: "
34+
msgstr "Definition: "
35+
36+
#: api/utils.py:186
37+
msgid "No definition is provided for this word."
38+
msgstr "Für dieses Wort ist keine Definition hinterlegt."
39+
40+
#: api/utils.py:187
41+
msgid "Training sets: "
42+
msgstr "Module: "
43+
44+
#: api/utils.py:191
45+
msgid "This word is not yet registered in the system"
46+
msgstr "Das Wort ist noch nicht im System hinterlegt."
47+
2448
#: api/v1/serializers/feedback_serializer.py:22
2549
msgid ""
2650
"The content type must be either 'discipline', 'training set' or 'document'."

lunes_cms/static/js/toggle_plural_field.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ if (!$) {
22
$ = django.jQuery;
33
}
44

5+
//Toggle the plural field depending on the chosen word type
56
$(document).ready(() => {
67
$("#id_word_type").change((event) =>
78
$("#id_plural")
@@ -11,6 +12,7 @@ $(document).ready(() => {
1112
$("#id_word_type").trigger("change");
1213
});
1314

15+
//Toggle the drop down for grammatical gender depending on the chosen word type
1416
$(document).ready(() => {
1517
$("#id_word_type").change((event) =>
1618
$("#id_grammatical_gender")
@@ -19,3 +21,77 @@ $(document).ready(() => {
1921
);
2022
$("#id_word_type").trigger("change");
2123
});
24+
25+
function removeDuplicationCheckMessage(){
26+
var existingMessage = document.getElementById("result");
27+
28+
if (existingMessage) {
29+
existingMessage.remove();
30+
}
31+
}
32+
33+
function showDuplicates(data, parent) {
34+
removeDuplicationCheckMessage();
35+
36+
result = document.createElement("div");
37+
result.setAttribute("id", "result");
38+
39+
40+
if (data["word"]) {
41+
// If there is a duplicated word, use orange background
42+
result.style.backgroundColor = "#ffbb4a";
43+
result.style.padding = "25px"
44+
45+
// Show alert message
46+
messageBox = document.createElement("div");
47+
message = document.createTextNode(data["message"]);
48+
messageBox.append(message);
49+
result.append(messageBox);
50+
// Show the duplicated word with its word type
51+
wordBox = document.createElement("div");
52+
word = document.createTextNode(data["word"]);
53+
wordBox.style.fontWeight = "bold";
54+
wordBox.append(word);
55+
result.append(wordBox);
56+
// Show its definition too for more detail
57+
definitionBox = document.createElement("div");
58+
definition = document.createTextNode(data["definition"]);
59+
definitionBox.append(definition);
60+
result.append(definitionBox);
61+
// Show related training sets
62+
trainingSetsBox = document.createElement("div");
63+
trainingSets = document.createTextNode(data["training_sets"]);
64+
trainingSetsBox.append(trainingSets);
65+
result.append(trainingSetsBox);
66+
} else {
67+
// If there is no duplicate, use green background
68+
result.style.backgroundColor = "#72f399";
69+
result.style.padding = "25px"
70+
// Show message that no duplicate was found
71+
messageBox = document.createElement("div");
72+
message = document.createTextNode(data["message"]);
73+
messageBox.append(message);
74+
result.append(messageBox);
75+
}
76+
77+
parent.prepend(result);
78+
}
79+
80+
81+
$(document).ready(() => {
82+
$("#id_word").change((event) => {
83+
if ($(event.target).val().length > 0) {
84+
$.ajax({
85+
type: 'GET',
86+
url: '/api/search_duplicate/' + $(event.target).val(),
87+
dataType: "json",
88+
success: function(data) {
89+
showDuplicates(data, $(event.target).closest(".card-body"));
90+
}
91+
})
92+
} else {
93+
removeDuplicationCheckMessage();
94+
}
95+
});
96+
$("#id_word_type").trigger("change");
97+
});

tests/api/api_config.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,26 @@
388388
},
389389
]
390390

391+
392+
SEARCH_DUPLICATE_ENDPOINTS = [
393+
{
394+
"endpoint": "/api/search_duplicate/implementieren",
395+
"expected_result": "tests/api/expected-results/duplicate_implementieren.json",
396+
},
397+
{
398+
"endpoint": "/api/search_duplicate/Schere",
399+
"expected_result": "tests/api/expected-results/duplicate_Schere.json",
400+
},
401+
{
402+
"endpoint": "/api/search_duplicate/Ei",
403+
"expected_result": "tests/api/expected-results/duplicate_Ei.json",
404+
},
405+
{
406+
"endpoint": "/api/search_duplicate/neueswort",
407+
"expected_result": "tests/api/expected-results/duplicate_neueswort.json",
408+
},
409+
]
410+
391411
#: The API endpoints
392412
API_ENDPOINTS = (
393413
DISCIPLINE_ENDPOINTS
@@ -396,6 +416,7 @@
396416
+ GROUP_ENDPOINTS
397417
+ FEEDBACK_ENDPOINTS
398418
+ SPONSOR_ENDPOINTS
419+
+ SEARCH_DUPLICATE_ENDPOINTS
399420
)
400421

401422
#: Convert the dicts to tuples with a fixed length
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"message": "This word is already registered in the system.",
3+
"word": "📷 (das) Ei (Nomen)",
4+
"definition": "Definition: Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua",
5+
"training_sets": "Training sets: Zutaten"
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"message": "This word is already registered in the system.",
3+
"word": "📷 (die) Schere (Nomen)",
4+
"definition": "Definition: No definition is provided for this word.",
5+
"training_sets": "Training sets: Grundlagen Werkzeuge, Grundlagen Rezeption"
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"message": "This word is already registered in the system.",
3+
"word": "⚠ (keiner) implementieren (Verb)",
4+
"definition": "Definition: in ein bestehendes Computersystem einsetzen, einbauen und so ein funktionsfähiges Programm erstellen",
5+
"training_sets": "Training sets: This word is assigned to no training set."
6+
}

0 commit comments

Comments
 (0)