Skip to content

Commit 5bc7eff

Browse files
committed
#1463, #1461, #1484: Code for multilingual gloss update translation code
renamed functions to make it clear what is being done. Added status code 400 to error branches to make sure failure of the operation is observed by caller. also fail for transaction error on save.
1 parent 4e3eeb3 commit 5bc7eff

File tree

2 files changed

+70
-68
lines changed

2 files changed

+70
-68
lines changed

signbank/dictionary/views.py

-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
from django.utils.timezone import get_current_timezone
5151

5252
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist, BadRequest
53-
from signbank.gloss_update import api_update_gloss_fields
5453
from django.utils.translation import gettext_lazy as _, activate
5554
from signbank.abstract_machine import get_interface_language_api
5655

signbank/gloss_update.py

+70-67
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import base64
2222

2323

24-
def api_update_gloss_fields(dataset, language_code='en'):
24+
def names_of_update_gloss_fields(dataset, language_code):
2525
activate(language_code)
2626

2727
dataset_languages = dataset.translation_languages.all()
@@ -32,18 +32,15 @@ def api_update_gloss_fields(dataset, language_code='en'):
3232

3333
language_fields = annotationidglosstranslation_fields + lemmaidglosstranslation_fields
3434

35-
api_fields_2024 = []
36-
fieldnames = FIELDS['main'] + FIELDS['phonology'] + FIELDS['semantics'] + ['inWeb', 'isNew', 'excludeFromEcv', 'senses']
35+
verbose_field_names = []
36+
fieldnames = FIELDS['main'] + FIELDS['phonology'] + FIELDS['semantics'] + ['inWeb', 'isNew', 'excludeFromEcv']
3737
gloss_fields = [Gloss.get_field(fname) for fname in fieldnames if fname in Gloss.get_field_names()]
3838

39-
# TO DO
40-
extra_columns = ['Sign Languages', 'Dialects',
41-
'Relations to other signs', 'Relations to foreign signs', 'Tags', 'Notes']
42-
39+
activate(language_code)
4340
for field in gloss_fields:
44-
api_fields_2024.append(field.verbose_name.title())
41+
verbose_field_names.append(field.verbose_name.title())
4542

46-
return language_fields, api_fields_2024
43+
return language_fields, verbose_field_names
4744

4845

4946
def internal_language_fields(dataset, language_code):
@@ -63,7 +60,7 @@ def internal_language_fields(dataset, language_code):
6360
def update_gloss_columns_to_value_dict_keys(dataset, language_code):
6461
"""
6562
Function to create mapping dictionaries for the different label representations
66-
Called from gloss_update
63+
Called from gloss_pre_update
6764
Representations:
6865
1. Human readable labels (multilingual, with language name in parentheses)
6966
2. JSON keys (multilingual, with colon before language name)
@@ -101,7 +98,7 @@ def update_gloss_columns_to_value_dict_keys(dataset, language_code):
10198
human_readable_to_json[human_readable_lemma] = lemma_api_field_name
10299
human_readable_to_json[human_readable_annotation] = annotation_api_field_name
103100

104-
fieldnames = FIELDS['main'] + FIELDS['phonology'] + FIELDS['semantics'] + ['inWeb', 'isNew', 'excludeFromEcv', 'senses']
101+
fieldnames = FIELDS['main'] + FIELDS['phonology'] + FIELDS['semantics'] + ['inWeb', 'isNew', 'excludeFromEcv']
105102
gloss_fields = [Gloss.get_field(fname) for fname in fieldnames if fname in Gloss.get_field_names()]
106103

107104
for field in gloss_fields:
@@ -116,21 +113,21 @@ def get_gloss_update_human_readable_value_dict(request):
116113
value_dict = dict()
117114
for field in post_data.keys():
118115
value = post_data.get(field, '')
119-
value_dict[field] = value.strip()
116+
value_dict[field] = value
120117
return value_dict
121118

122119

123120
def check_fields_can_be_updated(value_dict, dataset, language_code):
124-
language_fields, api_fields_2024 = api_update_gloss_fields(dataset, language_code)
121+
activate(language_code)
122+
language_fields, gloss_fields = names_of_update_gloss_fields(dataset, language_code)
123+
available_fields = language_fields + gloss_fields
125124
errors = dict()
126125
for field in value_dict.keys():
127-
if field not in api_fields_2024 and field not in language_fields:
128-
errors[field] = _("Field update not available")
129-
if field == "Senses":
130-
new_senses, formatting_error_senses = convert_string_to_dict_of_list_of_lists(value_dict[field])
131-
if formatting_error_senses:
132-
errors[field] = formatting_error_senses
133-
126+
if field in gloss_fields or field in language_fields:
127+
continue
128+
errors[field] = _("Field name not found in fields that can be updated.")
129+
if errors:
130+
errors["Available Fields"] = ", ".join(available_fields)
134131
return errors
135132

136133

@@ -311,25 +308,29 @@ def detect_type_related_problems_for_gloss_update(changes, dataset, language_cod
311308
continue
312309
if field in language_fields:
313310
continue
314-
if field in ['Senses', 'senses']:
315-
continue
316311
if isinstance(field, FieldChoiceForeignKey):
317312
field_choice_category = field.field_choice_category
318313
try:
319314
fieldchoice = FieldChoice.objects.get(field=field_choice_category, name__iexact=new_value)
315+
continue
320316
except (ObjectDoesNotExist, MultipleObjectsReturned):
321317
normalised_choice = normalize_field_choice(new_value)
322318
try:
323319
fieldchoice = FieldChoice.objects.get(field=field_choice_category, name__iexact=normalised_choice)
320+
continue
324321
except (ObjectDoesNotExist, MultipleObjectsReturned):
325322
errors[field.verbose_name.title()] = gettext('NOT FOUND: ') + new_value
326-
elif isinstance(field, models.ForeignKey) and field.related_model == Handshape:
323+
continue
324+
if isinstance(field, models.ForeignKey) and field.related_model == Handshape:
327325
try:
328326
handshape = Handshape.objects.get(name__iexact=new_value)
327+
continue
329328
except (ObjectDoesNotExist, MultipleObjectsReturned):
330329
errors[field.verbose_name.title()] = gettext('NOT FOUND: ') + new_value
331-
elif field.__class__.__name__ == 'BooleanField':
332-
if new_value not in ['true', 'True', 'TRUE', 'false', 'False', 'FALSE', 'Neutral', 'None']:
330+
continue
331+
if field.__class__.__name__ == 'BooleanField':
332+
normalised_boolean_string = new_value.lower()
333+
if normalised_boolean_string not in ['true', 'false', 'neutral', 'ja', 'nee']:
333334
errors[field.verbose_name.title()] = gettext('NOT FOUND: ') + new_value
334335
elif field.name == 'semField':
335336
type_check = type_check_multiselect('SemField', new_value, language_code)
@@ -517,50 +518,42 @@ def get_original_senses_value(gloss):
517518
return senses_dict
518519

519520

520-
def gloss_update(gloss, update_fields_dict, language_code):
521+
def gloss_pre_update(gloss, input_fields_dict, language_code):
521522

522523
dataset = gloss.lemma.dataset
523-
language_fields, api_fields_2024 = api_update_gloss_fields(dataset, language_code)
524+
language_fields, gloss_fields = names_of_update_gloss_fields(dataset, language_code)
524525
human_readable_to_internal, human_readable_to_json = update_gloss_columns_to_value_dict_keys(dataset, language_code)
525-
combined_fields = api_fields_2024
526+
# initial value of fields is put in combined_fields variable
527+
combined_fields = gloss_fields
526528
for language_field in language_fields:
527529
gloss_dict_language_field = human_readable_to_json[language_field]
528530
combined_fields.append(gloss_dict_language_field)
529-
gloss_data_dict = gloss.get_fields_dict(combined_fields, language_code)
531+
# retrieve what the packages knows for these fields
532+
gloss_package_data_dict = gloss.get_fields_dict(combined_fields, language_code)
530533
fields_to_update = dict()
531-
for human_readable_field, new_field_value in update_fields_dict.items():
534+
for human_readable_field, new_field_value in input_fields_dict.items():
532535
if not new_field_value:
533536
continue
537+
if human_readable_field not in gloss_package_data_dict.keys():
538+
# ignore the field if it's not visible to the API user
539+
continue
540+
original_value = gloss_package_data_dict[human_readable_field]
541+
534542
if human_readable_field in language_fields:
535543
gloss_language_field = human_readable_to_json[human_readable_field]
536-
original_value = gloss_data_dict[gloss_language_field]
537544
gloss_field = human_readable_to_internal[human_readable_field]
538545
fields_to_update[gloss_field] = (original_value, new_field_value)
539546
continue
540-
if human_readable_field == 'Senses':
541-
original_value = get_original_senses_value(gloss)
542-
new_senses, errors = convert_string_to_dict_of_list_of_lists(new_field_value)
543-
if errors:
544-
# already checked at a previous step
545-
continue
546-
if original_value != new_senses:
547-
fields_to_update[human_readable_field] = (original_value, new_senses)
547+
if human_readable_field not in human_readable_to_internal.keys():
548+
# if this field is not available, ignore it
548549
continue
549-
elif human_readable_field not in gloss_data_dict.keys():
550-
# new value
551-
original_value = ''
552-
gloss_field = human_readable_to_internal[human_readable_field]
553-
fields_to_update[gloss_field] = (original_value, new_field_value)
550+
if original_value in ['False'] and new_field_value in ['', 'False']:
551+
# treat empty as False
554552
continue
555-
if human_readable_field in human_readable_to_internal.keys():
556-
original_value = gloss_data_dict[human_readable_field]
557-
if original_value in ['False'] and new_field_value in ['', 'False']:
558-
# treat empty as False
559-
continue
560-
if new_field_value == original_value:
561-
continue
562-
gloss_field = human_readable_to_internal[human_readable_field]
563-
fields_to_update[gloss_field] = (original_value, new_field_value)
553+
if new_field_value == original_value:
554+
continue
555+
gloss_field = human_readable_to_internal[human_readable_field]
556+
fields_to_update[gloss_field] = (original_value, new_field_value)
564557
return fields_to_update
565558

566559

@@ -583,14 +576,14 @@ def api_update_gloss(request, datasetid, glossid):
583576
errors[gettext("Dataset")] = gettext("Dataset ID does not exist.")
584577
results['errors'] = errors
585578
results['updatestatus'] = "Failed"
586-
return JsonResponse(results, safe=False)
579+
return JsonResponse(results, status=400)
587580

588581
change_permit_datasets = get_objects_for_user(request.user, 'change_dataset', Dataset)
589582
if dataset not in change_permit_datasets:
590583
errors[gettext("Dataset")] = gettext("No change permission for dataset.")
591584
results['errors'] = errors
592585
results['updatestatus'] = "Failed"
593-
return JsonResponse(results, safe=False)
586+
return JsonResponse(results, status=400)
594587

595588
try:
596589
gloss_id = int(glossid)
@@ -600,60 +593,70 @@ def api_update_gloss(request, datasetid, glossid):
600593
errors[gettext("Gloss")] = gettext("Gloss ID must be a number.")
601594
results['errors'] = errors
602595
results['updatestatus'] = "Failed"
603-
return JsonResponse(results, safe=False)
596+
return JsonResponse(results, status=400)
604597

605598
gloss = Gloss.objects.filter(id=gloss_id, archived=False).first()
606599

607600
if not gloss:
608601
errors[gettext("Gloss")] = gettext("Gloss not found.")
609602
results['errors'] = errors
610603
results['updatestatus'] = "Failed"
611-
return JsonResponse(results, safe=False)
604+
return JsonResponse(results, status=400)
612605

613606
if not gloss.lemma:
614607
errors[gettext("Gloss")] = gettext("Gloss does not have a lemma.")
615608
results['errors'] = errors
616609
results['updatestatus'] = "Failed"
617-
return JsonResponse(results, safe=False)
610+
return JsonResponse(results, status=400)
618611

619612
if gloss.lemma.dataset != dataset:
620613
errors[gettext("Gloss")] = gettext("Gloss not found in the dataset.")
621614
results['errors'] = errors
622615
results['updatestatus'] = "Failed"
623-
return JsonResponse(results, safe=False)
616+
return JsonResponse(results, status=400)
624617

625618
if not request.user.has_perm('dictionary.change_gloss'):
626619
errors[gettext("Gloss")] = gettext("No change gloss permission.")
627620
results['errors'] = errors
628621
results['updatestatus'] = "Failed"
629-
return JsonResponse(results, safe=False)
622+
return JsonResponse(results, status=400)
630623

631624
value_dict = get_gloss_update_human_readable_value_dict(request)
625+
632626
errors = check_fields_can_be_updated(value_dict, dataset, interface_language_code)
633627
if errors:
628+
# "Field name not found in fields that can be updated."
634629
results['errors'] = errors
635630
results['updatestatus'] = "Failed"
636-
return JsonResponse(results, safe=False)
631+
return JsonResponse(errors, status=400)
637632

638-
fields_to_update = gloss_update(gloss, value_dict, interface_language_code)
633+
fields_to_update = gloss_pre_update(gloss, value_dict, interface_language_code)
639634
errors = detect_type_related_problems_for_gloss_update(fields_to_update, dataset, interface_language_code)
640635
if errors:
636+
# "New value has wrong type."
641637
results['errors'] = errors
642638
results['updatestatus'] = "Failed"
643-
return JsonResponse(results, safe=False)
639+
return JsonResponse(results, status=400)
644640

645641
errors = check_constraints_on_gloss_language_fields(gloss, interface_language_code, fields_to_update)
646642
if errors:
643+
# "Constraint violation on language fields."
647644
results['errors'] = errors
648645
results['updatestatus'] = "Failed"
649-
return JsonResponse(results, safe=False)
646+
return JsonResponse(results, status=400)
650647

651-
gloss_update_do_changes(request.user, gloss, fields_to_update, interface_language_code)
648+
try:
649+
gloss_update_do_changes(request.user, gloss, fields_to_update, interface_language_code)
650+
except (TransactionManagementError, ValueError, IntegrityError):
651+
errors['Exception'] = "Transaction management error."
652+
results['errors'] = errors
653+
results['updatestatus'] = "Failed"
654+
return JsonResponse(results, status=400)
652655

653656
results['errors'] = {}
654-
results['updatestatus'] = "Success"
657+
results['updatestatus'] = "Gloss successfully updated."
655658

656-
return JsonResponse(results, safe=False)
659+
return JsonResponse(results, status=201)
657660

658661

659662
def check_confirmed(value_dict):

0 commit comments

Comments
 (0)