Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle errors with improper chant text field characters/syllabification fails #1653

Merged
merged 5 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 85 additions & 27 deletions django/cantusdb_project/main_app/forms.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
from django import forms
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth import get_user_model
from django.db.models import Q
from django.contrib.admin.widgets import (
FilteredSelectMultiple,
)
from django.forms.widgets import CheckboxSelectMultiple
from dal import autocomplete
from volpiano_display_utilities.cantus_text_syllabification import syllabify_text
from volpiano_display_utilities.latin_word_syllabification import LatinError
from .models import (
Chant,
Service,
Expand All @@ -22,13 +31,6 @@
SelectWidget,
CheckboxWidget,
)
from django.contrib.auth import get_user_model
from django.db.models import Q
from django.contrib.admin.widgets import (
FilteredSelectMultiple,
)
from django.forms.widgets import CheckboxSelectMultiple
from dal import autocomplete

# ModelForm allows to build a form directly from a model
# see https://docs.djangoproject.com/en/3.0/topics/forms/modelforms/
Expand Down Expand Up @@ -71,6 +73,40 @@ def label_from_instance(self, obj):
widget = CheckboxSelectMultiple()



class CantusDBLatinField(forms.CharField):
"""
A custom CharField for chant text fields. Validates that the text
can be syllabified (essentially, that it does not have any improper
characters).
"""

def validate(self, value):
super().validate(value)
if value:
try:
syllabify_text(value)
except LatinError as err:
raise forms.ValidationError(str(err))
except ValueError as exc:
raise forms.ValidationError("Invalid characters in text.") from exc


class CantusDBSyllabifiedLatinField(forms.CharField):
"""
A custom CharField for chant syllabified text fields. Validates that the text
can be syllabified (essentially, that it does not have any improper
characters).
"""

def validate(self, value):
super().validate(value)
if value:
try:
syllabify_text(value, text_presyllabified=True)
except ValueError as exc:
raise forms.ValidationError("Invalid characters in text.") from exc

class StyledChoiceField(forms.ChoiceField):
"""
A custom ChoiceField that uses the custom SelectWidget defined in widgets.py
Expand All @@ -80,6 +116,7 @@ class StyledChoiceField(forms.ChoiceField):
widget = SelectWidget()



class ChantCreateForm(forms.ModelForm):
class Meta:
model = Chant
Expand Down Expand Up @@ -134,8 +171,8 @@ class Meta:
"finalis": TextInputWidget(),
"extra": TextInputWidget(),
"chant_range": VolpianoInputWidget(),
# manuscript_full_text_std_spelling: defined below (required)
"manuscript_full_text": TextAreaWidget(),
# manuscript_full_text_std_spelling: defined below (required & special field)
# "manuscript_full_text": defined below (special field)
"volpiano": VolpianoAreaWidget(),
"image_link": TextInputWidget(),
"melody_id": TextInputWidget(),
Expand All @@ -162,14 +199,18 @@ class Meta:
help_text="Each folio starts with '1'.",
)

manuscript_full_text_std_spelling = forms.CharField(
manuscript_full_text_std_spelling = CantusDBLatinField(
widget=TextAreaWidget,
help_text=Chant._meta.get_field("manuscript_full_text_std_spelling").help_text,
label="Full text as in Source (standardized spelling)",
required=True,
)

manuscript_full_text = CantusDBLatinField(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this.

widget=TextAreaWidget,
help_text="Manuscript full text with standardized spelling. Enter the words "
"according to the manuscript but normalize their spellings following "
"Classical Latin forms. Use upper-case letters for proper nouns, "
'the first word of each chant, and the first word after "Alleluia" for '
"Mass Alleluias. Punctuation is omitted.",
label="Full text as in Source (source spelling)",
help_text=Chant._meta.get_field("manuscript_full_text").help_text,
required=False,
)

project = SelectWidgetNameModelChoiceField(
Expand Down Expand Up @@ -318,8 +359,8 @@ class Meta:
"rubrics",
]
widgets = {
# manuscript_full_text_std_spelling: defined below (required)
"manuscript_full_text": TextAreaWidget(),
# manuscript_full_text_std_spelling: defined below (required) & special field
# manuscript_full_text: defined below (special field)
"volpiano": VolpianoAreaWidget(),
"marginalia": TextInputWidget(),
# folio: defined below (required)
Expand Down Expand Up @@ -353,14 +394,18 @@ class Meta:
"rubrics": TextInputWidget(),
}

manuscript_full_text_std_spelling = forms.CharField(
manuscript_full_text_std_spelling = CantusDBLatinField(
widget=TextAreaWidget,
help_text=Chant._meta.get_field("manuscript_full_text_std_spelling").help_text,
label="Full text as in Source (standardized spelling)",
required=True,
)

manuscript_full_text = CantusDBLatinField(
widget=TextAreaWidget,
help_text="Manuscript full text with standardized spelling. Enter the words "
"according to the manuscript but normalize their spellings following "
"Classical Latin forms. Use upper-case letters for proper nouns, "
'the first word of each chant, and the first word after "Alleluia" for '
"Mass Alleluias. Punctuation is omitted.",
label="Full text as in Source (source spelling)",
help_text=Chant._meta.get_field("manuscript_full_text").help_text,
required=False,
)

folio = forms.CharField(
Expand Down Expand Up @@ -531,10 +576,14 @@ class Meta:
"manuscript_full_text",
"manuscript_syllabized_full_text",
]
widgets = {
"manuscript_full_text": TextAreaWidget(),
"manuscript_syllabized_full_text": TextAreaWidget(),
}

manuscript_full_text = CantusDBLatinField(
widget=TextAreaWidget, label="Full text as in Source (source spelling)"
)

manuscript_syllabized_full_text = CantusDBSyllabifiedLatinField(
widget=TextAreaWidget, label="Syllabized full text"
)


class AdminCenturyForm(forms.ModelForm):
Expand Down Expand Up @@ -714,6 +763,15 @@ class Meta:
# help_text="RISM-style siglum + Shelf-mark (e.g. GB-Ob 202).",
# )


shelfmark = forms.CharField(
required=True,
widget=TextInputWidget,
)

name = forms.CharField(required=False, widget=TextInputWidget)


holding_institution = forms.ModelChoiceField(
queryset=Institution.objects.all().order_by("city", "name"),
required=False,
Expand Down
5 changes: 2 additions & 3 deletions django/cantusdb_project/main_app/templates/chant_create.html
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ <h3>Create Chant</h3>

<div class="form-row align-items-end">
<div class="form-group m-1 col-lg">
<label for="{{ form.manuscript_full_text_std_spelling.id_for_label }}"><small>Full text as in Source (standardized spelling):<span class="text-danger" title="This field is required">*</span></small></label>
<label for="{{ form.manuscript_full_text_std_spelling.id_for_label }}" class="small">{{ form.manuscript_full_text_std_spelling.label_tag }}<span class="text-danger" title="This field is required">*</span></label>
{{ form.manuscript_full_text_std_spelling }}
<p>
<small class="text-muted">{{ form.manuscript_full_text_std_spelling.help_text }}
Expand All @@ -241,8 +241,7 @@ <h3>Create Chant</h3>

<div class="form-row align-items-end">
<div class="form-group m-1 col-lg">
<label for="{{ form.manuscript_full_text.id_for_label }}"><small>Full text as in Source
(source spelling): </small></label>
<label for="{{ form.manuscript_full_text.id_for_label }}" class="small">{{ form.manuscript_full_text.label_tag }}</label>
{{ form.manuscript_full_text }}
<p class="text-muted" style="line-height: normal">
<small>{{ form.manuscript_full_text.help_text }}
Expand Down
35 changes: 22 additions & 13 deletions django/cantusdb_project/main_app/templates/chant_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<!--Display messages -->
{% for message in messages %}
<div class="alert {{ message.tags }} alert-dismissible" role="alert" >
<a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>
{{ message }}
</div>
{% endfor %}
Expand Down Expand Up @@ -283,25 +284,33 @@
<p><small>Syllabification is based on saved syllabized text.</small></p>
{% endif %}
<dd>
{% for syl_text, syl_mel in syllabized_text_with_melody %}
<span style="float: left">
<div style="font-family: volpiano; font-size: 36px">{{ syl_mel }}</div>
<!-- "mt" is margin at the top, so that the lowest note in volpiano don't overlap with text -->
<div class="mt-2" style="font-size: 12px; "><pre>{{ syl_text }}</pre></div>
</span>
{% endfor %}
{% if syllabized_text_with_melody %}
{% for syl_text, syl_mel in syllabized_text_with_melody %}
<span style="float: left">
<div style="font-family: volpiano; font-size: 36px">{{ syl_mel }}</div>
<!-- "mt" is margin at the top, so that the lowest note in volpiano don't overlap with text -->
<div class="mt-2" style="font-size: 12px; "><pre>{{ syl_text }}</pre></div>
</span>
{% endfor %}
{% else %}
<p>Error aligning text and melody. Please check text for invalid characters.</p>
{% endif %}
</dd>
</div>
</div>
{% endif %}

<div class="form-row">
<div class="form-group m-1 col-lg-4">
<a href="{% url "source-edit-syllabification" chant.id %}" style="display: inline-block; margin-top:5px;" target="_blank">
<small>Edit syllabification (new window)</small>
</a>
<!-- If there's no syllabized_text_with_melody (either there's no volpiano to align or there's an
error in the text), there's no need for the user to edit the syllabification. -->
{% if syllabized_text_with_melody %}
<div class="form-row">
<div class="form-group m-1 col-lg-4">
<a href="{% url "source-edit-syllabification" chant.id %}" style="display: inline-block; margin-top:5px;" target="_blank">
<small>Edit syllabification (new window)</small>
</a>
</div>
</div>
</div>
{% endif %}

<div class="form-row">
<div class="form-group m-1 col-lg-12">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<!--Display messages -->
{% for message in messages %}
<div class="alert {{ message.tags }} alert-dismissible" role="alert" >
<a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>s
{{ message }}
</div>
{% endfor %}
Expand All @@ -37,17 +38,17 @@ <h3>Edit Syllabification</h3>

<div class="form-row">
<div class="form-group m-1 col-lg-12">
<label for="{{ form.manuscript_full_text.id_for_label }}">
<small><b>Manuscript Reading Full Text (MS spelling):</b></small>
<label for="{{ form.manuscript_full_text.id_for_label }}" class="small font-weight-bold">
{{ form.manuscript_full_text.label_tag }}
</label>
{{ form.manuscript_full_text }}
</div>
</div>

<div class="form-row">
<div class="form-group m-1 col-lg-12">
<label for="{{ form.manuscript_syllabized_full_text.id_for_label }}">
<small><b>Syllabized Full Text:</b></small>
<label for="{{ form.manuscript_syllabized_full_text.id_for_label }}" class="small font-weight-bold">
{{ form.manuscript_syllabized_full_text.label_tag }}
</label>
{{ form.manuscript_syllabized_full_text }}
</div>
Expand Down
Loading
Loading