diff --git a/config/nginx/conf.d/cantusdb.conf.development b/config/nginx/conf.d/cantusdb.conf.development index 034e62f7c..51e2f12ce 100644 --- a/config/nginx/conf.d/cantusdb.conf.development +++ b/config/nginx/conf.d/cantusdb.conf.development @@ -1,41 +1,9 @@ # This file is configured for deployment of the CantusDB project in local development. # When building the project locally, replace the contents of cantusdb.conf with the # contents of this file. - -# server { -# listen 80; -# -# server_tokens off; -# -# location ^~ /.well-known/acme-challenge/ { -# root /var/www/lego; -# } -# -# location / { -# return 301 https://$host$request_uri; -# } -# } - -# server { -# # Redirect all https traffic for mass.cantusdatabase.org to cantusdatabase.org -# listen 443 ssl; -# -# server_name mass.cantusdatabase.org; -# -# ssl_certificate /etc/nginx/ssl/live/certificates/cantusdatabase.org.crt; -# ssl_certificate_key /etc/nginx/ssl/live/certificates/cantusdatabase.org.key; -# -# location / { -# return 301 https://cantusdatabase.org$request_uri; -# } -# } server { - # listen 443 default_server http2 ssl; listen 80; - - # ssl_certificate /etc/nginx/ssl/live/certificates/cantusdatabase.org.crt; - # ssl_certificate_key /etc/nginx/ssl/live/certificates/cantusdatabase.org.key; location / { proxy_pass http://django:8000; diff --git a/config/nginx/conf.d/cantusdb.conf.production b/config/nginx/conf.d/cantusdb.conf.production index b109ffedf..ec1107ede 100644 --- a/config/nginx/conf.d/cantusdb.conf.production +++ b/config/nginx/conf.d/cantusdb.conf.production @@ -31,10 +31,10 @@ server { } server { - listen 443 default_server http2 ssl; + listen 443 http2 ssl; server_name cantusdatabase.org; - + ssl_certificate /etc/nginx/ssl/live/certificates/cantusdatabase.org.crt; ssl_certificate_key /etc/nginx/ssl/live/certificates/cantusdatabase.org.key; @@ -56,7 +56,7 @@ server { alias /resources/api_cache/concordances.json; expires modified +24h; } - + location = /style.css { root /; } @@ -79,3 +79,18 @@ server { root /; } } + +# Default server to block requests to IP address and non-valid hostnames +server { + access_log off; + server_name _; + + listen 80 default_server; + listen 443 default_server; + ssl_reject_handshake on; + + # Close connection immediately for requests that don't match a defined + # server name + return 444; +} + diff --git a/config/nginx/conf.d/cantusdb.conf.staging b/config/nginx/conf.d/cantusdb.conf.staging index cf3e59a13..7377880f0 100644 --- a/config/nginx/conf.d/cantusdb.conf.staging +++ b/config/nginx/conf.d/cantusdb.conf.staging @@ -21,11 +21,8 @@ server { # The Staging server's subdomain "staging-alias" is analogous to Production's "mass" subdomain.) listen 443 ssl; - # server_name mass.cantusdatabase.org; server_name staging-alias.cantusdatabase.org; - # ssl_certificate /etc/nginx/ssl/live/certificates/cantusdatabase.org.crt; - # ssl_certificate_key /etc/nginx/ssl/live/certificates/cantusdatabase.org.key; ssl_certificate /etc/nginx/ssl/live/certificates/staging.cantusdatabase.org.crt; ssl_certificate_key /etc/nginx/ssl/live/certificates/staging.cantusdatabase.org.key; @@ -35,12 +32,10 @@ server { } server { - listen 443 default_server http2 ssl; + listen 443 http2 ssl; server_name staging.cantusdatabase.org; - - # ssl_certificate /etc/nginx/ssl/live/certificates/cantusdatabase.org.crt; - # ssl_certificate_key /etc/nginx/ssl/live/certificates/cantusdatabase.org.key; + ssl_certificate /etc/nginx/ssl/live/certificates/staging.cantusdatabase.org.crt; ssl_certificate_key /etc/nginx/ssl/live/certificates/staging.cantusdatabase.org.key; @@ -62,7 +57,7 @@ server { alias /resources/api_cache/concordances.json; expires modified +24h; } - + location = /style.css { root /; } @@ -85,3 +80,17 @@ server { root /; } } + +# Default server to block requests to IP address and non-valid hostnames +server { + access_log off; + server_name _; + + listen 80 default_server; + listen 443 default_server; + ssl_reject_handshake on; + + # Close connection immediately for requests that don't match a defined + # server name + return 444; +} diff --git a/django/cantusdb_project/main_app/admin.py b/django/cantusdb_project/main_app/admin.py index 45cff5f78..e23b07503 100644 --- a/django/cantusdb_project/main_app/admin.py +++ b/django/cantusdb_project/main_app/admin.py @@ -21,6 +21,8 @@ READ_ONLY = ( "created_by", "last_updated_by", + "date_created", + "date_updated", ) @@ -61,10 +63,7 @@ def get_source_siglum(self, obj): "id", ) - readonly_fields = READ_ONLY + ( - "date_created", - "date_updated", - ) + readonly_fields = READ_ONLY + ("incipit",) list_filter = ( "genre", @@ -170,6 +169,7 @@ def get_source_siglum(self, obj): "source", "feast", ) + readonly_fields = READ_ONLY + ("incipit",) ordering = ("source__siglum",) form = AdminSequenceForm diff --git a/django/cantusdb_project/main_app/signals.py b/django/cantusdb_project/main_app/signals.py index d5def7b75..a9879a4cd 100644 --- a/django/cantusdb_project/main_app/signals.py +++ b/django/cantusdb_project/main_app/signals.py @@ -7,6 +7,8 @@ from django.db.models.signals import post_save, post_delete from django.dispatch import receiver +from typing import Optional + import re from main_app.models import Chant @@ -16,36 +18,38 @@ @receiver(post_save, sender=Chant) -def on_chant_save(instance, **kwargs): +def on_chant_save(instance, **kwargs) -> None: update_source_chant_count(instance) update_source_melody_count(instance) update_chant_search_vector(instance) + update_chant_incipit_field(instance) update_volpiano_fields(instance) @receiver(post_delete, sender=Chant) -def on_chant_delete(instance, **kwargs): +def on_chant_delete(instance, **kwargs) -> None: update_source_chant_count(instance) update_source_melody_count(instance) @receiver(post_save, sender=Sequence) -def on_sequence_save(instance, **kwargs): +def on_sequence_save(instance, **kwargs) -> None: update_source_chant_count(instance) + update_sequence_incipit_field(instance) @receiver(post_delete, sender=Sequence) -def on_sequence_delete(instance, **kwargs): +def on_sequence_delete(instance, **kwargs) -> None: update_source_chant_count(instance) @receiver(post_save, sender=Feast) -def on_feast_save(instance, **kwargs): +def on_feast_save(instance, **kwargs) -> None: update_prefix_field(instance) -def update_chant_search_vector(instance): +def update_chant_search_vector(instance) -> None: """When saving an instance of Chant, update its search vector field. Called in on_chant_save() @@ -63,7 +67,7 @@ def update_chant_search_vector(instance): ) -def update_source_chant_count(instance): +def update_source_chant_count(instance) -> None: """When saving or deleting a Chant or Sequence, update its Source's number_of_chants field Called in on_chant_save(), on_chant_delete(), on_sequence_save() and on_sequence_delete() @@ -79,7 +83,7 @@ def update_source_chant_count(instance): source.save() -def update_source_melody_count(instance): +def update_source_melody_count(instance) -> None: """When saving or deleting a Chant, update its Source's number_of_melodies field Called in on_chant_save() and on_chant_delete() @@ -99,88 +103,12 @@ def update_source_melody_count(instance): source.save() -def update_volpiano_fields(instance): +def update_volpiano_fields(instance) -> None: """When saving a Chant, make sure the chant's volpiano_notes and volpiano_intervals are up-to-date Called in on_chant_save() """ - def generate_volpiano_notes(volpiano): - """ - Populate the ``volpiano_notes`` field of the ``Chant`` model - - This field is used for melody search - - Args: - volpiano (str): The content of ``chant.volpiano`` - - Returns: - str: Volpiano str with non-note chars and duplicate consecutive notes removed - """ - # unwanted_chars are non-note chars, including the clefs, barlines, and accidentals etc. - # the `searchMelody.js` on old cantus makes no reference to the b-flat accidentals ("y", "i", "z") - # so put them in unwanted chars for now - unwanted_chars = [ - "-", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "?", - ".", - " ", - "y", - "i", - "z", - ] - # convert all charactors to lower-case, upper-case letters stand for liquescent of the same pitch - volpiano_lower = volpiano.lower() - # `)` stands for the lowest `g` note liquescent in volpiano, its 'lower case' is `9` - volpiano_notes = volpiano_lower.replace(")", "9") - # remove none-note charactors - for unwanted_char in unwanted_chars: - volpiano_notes = volpiano_notes.replace(unwanted_char, "") - # remove duplicate consecutive chars - volpiano_notes = re.sub(r"(.)\1+", r"\1", volpiano_notes) - return volpiano_notes - - def generate_volpiano_intervals(volpiano_notes): - """ - Populate the ``volpiano_intervals`` field of the ``Chant`` model - - This field is used for melody search when searching for transpositions - - Args: - volpiano_notes (str): The content of ``chant.volpiano_notes``, - populated by the ``generate_volpiano_notes`` function - - Returns: - str: A str of digits, recording the intervals between adjacent notes - """ - # replace '9' (the note G) with the char corresponding to (ASCII(a) - 1), because 'a' denotes the note A - volpiano_notes = volpiano_notes.replace("9", chr(ord("a") - 1)) - # we model the interval between notes using the difference between the ASCII codes of corresponding letters - # the letter for the note B is "j" (106), note A is "h" (104), the letter "i" (105) is skipped - # move all notes above A down by one letter - volpiano_notes = list(volpiano_notes) - for j, note in enumerate(volpiano_notes): - if ord(note) >= 106: - volpiano_notes[j] = chr(ord(note) - 1) - - # `intervals` records the difference between two adjacent notes. - # Note that intervals are encoded by counting the number of scale - # steps between adjacent notes: an ascending second is thus encoded - # as "1"; a descending third is encoded "-2", and so on. - intervals = [] - for j in range(1, len(volpiano_notes)): - intervals.append(ord(volpiano_notes[j]) - ord(volpiano_notes[j - 1])) - # convert `intervals` to str - volpiano_intervals = "".join([str(interval) for interval in intervals]) - return volpiano_intervals - if instance.volpiano is None: return @@ -193,7 +121,84 @@ def generate_volpiano_intervals(volpiano_notes): ) -def update_prefix_field(instance): +def generate_volpiano_notes(volpiano) -> str: + """ + Populate the ``volpiano_notes`` field of the ``Chant`` model + + This field is used for melody search + + Args: + volpiano (str): The content of ``chant.volpiano`` + + Returns: + str: Volpiano str with non-note chars and duplicate consecutive notes removed + """ + # unwanted_chars are non-note chars, including the clefs, barlines, and accidentals etc. + # the `searchMelody.js` on old cantus makes no reference to the b-flat accidentals ("y", "i", "z") + # so put them in unwanted chars for now + unwanted_chars: list[str] = [ + "-", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "?", + ".", + " ", + "y", + "i", + "z", + ] + # convert all charactors to lower-case, upper-case letters stand for liquescent of the same pitch + volpiano_lower: str = volpiano.lower() + # `)` stands for the lowest `g` note liquescent in volpiano, its 'lower case' is `9` + volpiano_notes: str = volpiano_lower.replace(")", "9") + # remove none-note charactors + for unwanted_char in unwanted_chars: + volpiano_notes = volpiano_notes.replace(unwanted_char, "") + # remove duplicate consecutive chars + volpiano_notes = re.sub(r"(.)\1+", r"\1", volpiano_notes) + return volpiano_notes + + +def generate_volpiano_intervals(volpiano_notes) -> str: + """ + Populate the ``volpiano_intervals`` field of the ``Chant`` model + + This field is used for melody search when searching for transpositions + + Args: + volpiano_notes (str): The content of ``chant.volpiano_notes``, + populated by the ``generate_volpiano_notes`` function + + Returns: + str: A str of digits, recording the intervals between adjacent notes + """ + # replace '9' (the note G) with the char corresponding to (ASCII(a) - 1), because 'a' denotes the note A + volpiano_notes: str = volpiano_notes.replace("9", chr(ord("a") - 1)) + # we model the interval between notes using the difference between the ASCII codes of corresponding letters + # the letter for the note B is "j" (106), note A is "h" (104), the letter "i" (105) is skipped + # move all notes above A down by one letter + notes_list: list = list(volpiano_notes) + for j, note in enumerate(notes_list): + if ord(note) >= 106: + notes_list[j] = chr(ord(note) - 1) + + # `intervals` records the difference between two adjacent notes. + # Note that intervals are encoded by counting the number of scale + # steps between adjacent notes: an ascending second is thus encoded + # as "1"; a descending third is encoded "-2", and so on. + intervals: list[int] = [] + for j in range(1, len(notes_list)): + intervals.append(ord(notes_list[j]) - ord(notes_list[j - 1])) + volpiano_intervals: str = "".join([str(interval) for interval in intervals]) + return volpiano_intervals + + +def update_prefix_field(instance) -> None: pk = instance.pk if instance.feast_code: @@ -201,3 +206,53 @@ def update_prefix_field(instance): instance.__class__.objects.filter(pk=pk).update(prefix=prefix) else: # feast_code is None, "" instance.__class__.objects.filter(pk=pk).update(prefix="") + + +def update_chant_incipit_field(chant: Chant) -> None: + """Update the incipit field of the specified Chant to be the first + several words of the chant's standardized-spelling fulltext + + Args: + chant (Chant): The chant from the database whose `incipit` field + is to be updated + """ + fulltext: Optional[str] = chant.manuscript_full_text_std_spelling + if fulltext: # many chants in the database have only an incipit - + # we should only update the incipit if the chant has a fulltext, + # just in case a chant manages to get saved without a fulltext somehow + new_incipit: str = generate_incipit(fulltext) + Chant.objects.filter(id=chant.id).update(incipit=new_incipit) + + +def update_sequence_incipit_field(sequence: Sequence) -> None: + """Update the incipit field of the specified Sequence to be the first + several words of the sequence's standardized-spelling fulltext + + Args: + sequence (Sequence): The sequence from the database whose `incipit` + field is to be updated + """ + title: Optional[str] = sequence.title + if title: # As of late Feb 2024, no sequences in the database have + # fulltext, but every sequence has a title, and the value stored in + # the title field is an incipit. + incipit: str = title + Sequence.objects.filter(id=sequence.id).update(incipit=incipit) + + +def generate_incipit(fulltext: str) -> str: + """Given the fulltext of a chant or sequence, generate an incipit + consisting of the first 5 words of the fulltext. + + Args: + fulltext (str): the full text of a chant or sequence + + Returns: + str: an incipit - the first five words of the fulltext + """ + INCIPIT_LENGTH: int = 5 # number of words to include in the new incipit + + fulltext_words: list[str] = fulltext.split(" ") + incipit_words: list[str] = fulltext_words[:INCIPIT_LENGTH] + incipit: str = " ".join(incipit_words) + return incipit diff --git a/django/cantusdb_project/main_app/templates/400.html b/django/cantusdb_project/main_app/templates/400.html index ac1d64bf8..3ee52993d 100644 --- a/django/cantusdb_project/main_app/templates/400.html +++ b/django/cantusdb_project/main_app/templates/400.html @@ -25,10 +25,11 @@ {% block content %}
-
+

400

Bad Request

+

Your request couldn't be understood by the server.

diff --git a/django/cantusdb_project/main_app/templates/403.html b/django/cantusdb_project/main_app/templates/403.html index 9fd647296..b5738b049 100644 --- a/django/cantusdb_project/main_app/templates/403.html +++ b/django/cantusdb_project/main_app/templates/403.html @@ -2,7 +2,7 @@ {% block content %}
-
+

403

Access denied

diff --git a/django/cantusdb_project/main_app/templates/404.html b/django/cantusdb_project/main_app/templates/404.html index c9d8bff2d..0693d0593 100644 --- a/django/cantusdb_project/main_app/templates/404.html +++ b/django/cantusdb_project/main_app/templates/404.html @@ -2,13 +2,13 @@ {% block content %}
-
+

404

Page not found

-
-
{{ exception }}
-
+

+ We couldn't find a page at this address. +

diff --git a/django/cantusdb_project/main_app/templates/chant_delete.html b/django/cantusdb_project/main_app/templates/chant_delete.html index 4811523fb..de8c12420 100644 --- a/django/cantusdb_project/main_app/templates/chant_delete.html +++ b/django/cantusdb_project/main_app/templates/chant_delete.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block content %} Delete Chant | Cantus Manuscript Database -
+
{% csrf_token %}

diff --git a/django/cantusdb_project/main_app/templates/chant_detail.html b/django/cantusdb_project/main_app/templates/chant_detail.html index ff716190f..87d4681ca 100644 --- a/django/cantusdb_project/main_app/templates/chant_detail.html +++ b/django/cantusdb_project/main_app/templates/chant_detail.html @@ -430,7 +430,11 @@

List of melodies

diff --git a/django/cantusdb_project/main_app/templates/content_overview.html b/django/cantusdb_project/main_app/templates/content_overview.html index 92b15aa51..371c2495f 100644 --- a/django/cantusdb_project/main_app/templates/content_overview.html +++ b/django/cantusdb_project/main_app/templates/content_overview.html @@ -3,7 +3,7 @@ {% block content %} Content Overview | Cantus Manuscript Database -
+

Content Overview

@@ -19,80 +19,82 @@

Content Overview

{% if selected_model_name %} - - Displaying {{ page_obj.start_index }}-{{ page_obj.end_index }} of {{ page_obj.paginator.count }} - {{ selected_model_name|capfirst }} - - - - - - - - - - - - - {% for object in page_obj %} +
+
Title / Incipit / NameTypeCreation DateCreatorLast Updated DateLast Updated ByOperations
+ Displaying {{ page_obj.start_index }}-{{ page_obj.end_index }} of {{ page_obj.paginator.count }} + {{ selected_model_name|capfirst }} + - {% if object.title %} - - {% elif object.incipit %} - - {% elif object.name %} + + + + + + + + + + + {% for object in page_obj %} + + {% if object.title %} + + {% elif object.incipit %} + + {% elif object.name %} + + {% elif object.full_name %} + + {% else %} + + {% endif %} + + - {% elif object.full_name %} + - {% else %} - {% endif %} - - - - - - - - {% endfor %} - -
- {{ object.title|truncatechars:30 }} - - {{ object.incipit|truncatechars:30 }} - Title / Incipit / NameTypeCreation DateCreatorLast Updated DateLast Updated ByOperations
+ {{ object.title|truncatechars:30 }} + + {{ object.incipit|truncatechars:30 }} + + {% if object|classname == "Notation" or object|classname == "Segment" or object|classname == "RismSiglum" %} + {{ object.name|truncatechars:30 }} + {% else %} + {{ object.name|truncatechars:30 }} + {% endif %} + + {{ object.full_name }} + + {{ object|classname }} Object + {{ object|classname }}{{ object.date_created|date:'Y-m-d H:i' }} - {% if object|classname == "Notation" or object|classname == "Segment" or object|classname == "RismSiglum" %} - {{ object.name|truncatechars:30 }} + {% if object.created_by is None %} + {{ object.created_by }} {% else %} - {{ object.name|truncatechars:30 }} + + {{ object.created_by }} + {% endif %} {{ object.date_updated|date:'Y-m-d H:i' }} - {{ object.full_name }} + {% if object.last_updated_by is None %} + {{ object.last_updated_by }} + {% else %} + + {{ object.last_updated_by }} + + {% endif %} - {{ object|classname }} Object + {% with class=object|classname %} + Edit + | + Delete + {% endwith %} {{ object|classname }}{{ object.date_created|date:'Y-m-d H:i' }} - {% if object.created_by is None %} - {{ object.created_by }} - {% else %} - - {{ object.created_by }} - - {% endif %} - {{ object.date_updated|date:'Y-m-d H:i' }} - {% if object.last_updated_by is None %} - {{ object.last_updated_by }} - {% else %} - - {{ object.last_updated_by }} - - {% endif %} - - {% with class=object|classname %} - Edit - | - Delete - {% endwith %} -
+ + {% endfor %} + + +
{% include "pagination.html" %} {% else %}
diff --git a/django/cantusdb_project/main_app/templates/indexer_list.html b/django/cantusdb_project/main_app/templates/indexer_list.html index 6545f2116..c81bb2ba5 100644 --- a/django/cantusdb_project/main_app/templates/indexer_list.html +++ b/django/cantusdb_project/main_app/templates/indexer_list.html @@ -31,7 +31,11 @@

List of Indexers

{% for indexer in indexers %} - {{ indexer.full_name }} + {% if indexer.full_name %} + {{ indexer.full_name }} + {% else %} + User {{ indexer.id }} + {% endif %} {{ indexer.institution|default:"" }} {{ indexer.city|default:"" }} diff --git a/django/cantusdb_project/main_app/templates/registration/change_password.html b/django/cantusdb_project/main_app/templates/registration/change_password.html index 3d6218840..bee571e3c 100644 --- a/django/cantusdb_project/main_app/templates/registration/change_password.html +++ b/django/cantusdb_project/main_app/templates/registration/change_password.html @@ -2,7 +2,7 @@ {% block content %}
-
+
{% for message in messages %} diff --git a/django/cantusdb_project/main_app/templates/registration/login.html b/django/cantusdb_project/main_app/templates/registration/login.html index 3bf10c2bb..c70676679 100644 --- a/django/cantusdb_project/main_app/templates/registration/login.html +++ b/django/cantusdb_project/main_app/templates/registration/login.html @@ -3,7 +3,7 @@ Log in | Cantus Manuscript Database
-
+
{% if messages %}
diff --git a/django/cantusdb_project/main_app/templates/registration/reset_password.html b/django/cantusdb_project/main_app/templates/registration/reset_password.html index cc419fafd..e7a82f228 100644 --- a/django/cantusdb_project/main_app/templates/registration/reset_password.html +++ b/django/cantusdb_project/main_app/templates/registration/reset_password.html @@ -4,7 +4,7 @@ Request Password Reset | Cantus Manuscript Database
-
+
{% for message in messages %} diff --git a/django/cantusdb_project/main_app/templates/registration/reset_password_complete.html b/django/cantusdb_project/main_app/templates/registration/reset_password_complete.html index 34186d889..82472d321 100644 --- a/django/cantusdb_project/main_app/templates/registration/reset_password_complete.html +++ b/django/cantusdb_project/main_app/templates/registration/reset_password_complete.html @@ -4,7 +4,7 @@ Reset Password Complete | Cantus Manuscript Database
-
+

Your password has been set. You may go ahead and log in now.

Log in

diff --git a/django/cantusdb_project/main_app/templates/registration/reset_password_confirm.html b/django/cantusdb_project/main_app/templates/registration/reset_password_confirm.html index a68933226..f46d59567 100644 --- a/django/cantusdb_project/main_app/templates/registration/reset_password_confirm.html +++ b/django/cantusdb_project/main_app/templates/registration/reset_password_confirm.html @@ -4,7 +4,7 @@ Reset Password | Cantus Manuscript Database
-
+
{% if validlink %}

Please enter your new password twice so we can verify you typed it in correctly.

{% csrf_token %} diff --git a/django/cantusdb_project/main_app/templates/registration/reset_password_sent.html b/django/cantusdb_project/main_app/templates/registration/reset_password_sent.html index a9503ea5a..e431c4450 100644 --- a/django/cantusdb_project/main_app/templates/registration/reset_password_sent.html +++ b/django/cantusdb_project/main_app/templates/registration/reset_password_sent.html @@ -4,7 +4,7 @@ Password Reset Email Sent | Cantus Manuscript Database
-
+
Password Reset Email Sent

We’ve emailed you instructions for resetting your password, if an account exists with the email you entered. You should receive them shortly.

diff --git a/django/cantusdb_project/main_app/templates/sequence_list.html b/django/cantusdb_project/main_app/templates/sequence_list.html index fc1d0c0df..8aeceeee1 100644 --- a/django/cantusdb_project/main_app/templates/sequence_list.html +++ b/django/cantusdb_project/main_app/templates/sequence_list.html @@ -28,58 +28,60 @@

Clavis Sequentiarum (Calvin Bower)

{% if sequences %} - - - - - - - - - - - - - - - {% for sequence in sequences %} - - - - - - - - - +
+
SiglumText incipitRubricsAHCantus IDNotes:1Notes:2Notes:3
- {% if sequence.source %} - - {{ sequence.source.siglum }}
-
- {% endif %} - {{ sequence.folio|default:"" }} {{ sequence.s_sequence|default:"" }} -
- - {{ sequence.incipit|default:"" }} - - - {{ sequence.rubrics|default:"" }} - - {{ sequence.analecta_hymnica|default:"" }} - - {% comment %} use `urlencode` filter because 1 chant and 2 sequences have forward slash in their cantus_id (data error) {% endcomment %} - {{ sequence.cantus_id|default:"" }} - - {{ sequence.col1 |default:"" }} - - {{ sequence.col2 |default:"" }} - - {{ sequence.col3 |default:"" }} -
+ + + + + + + + + + - {% endfor %} - -
SiglumText incipitRubricsAHCantus IDNotes:1Notes:2Notes:3
+ + + {% for sequence in sequences %} + + + {% if sequence.source %} + + {{ sequence.source.siglum }}
+
+ {% endif %} + {{ sequence.folio|default:"" }} {{ sequence.s_sequence|default:"" }} + + + + {{ sequence.incipit|default:"" }} + + + + {{ sequence.rubrics|default:"" }} + + + {{ sequence.analecta_hymnica|default:"" }} + + + {% comment %} use `urlencode` filter because 1 chant and 2 sequences have forward slash in their cantus_id (data error) {% endcomment %} + {{ sequence.cantus_id|default:"" }} + + + {{ sequence.col1 |default:"" }} + + + {{ sequence.col2 |default:"" }} + + + {{ sequence.col3 |default:"" }} + + + {% endfor %} + + +
{% else %} No sequences found. {% endif %} diff --git a/django/cantusdb_project/main_app/templates/source_delete.html b/django/cantusdb_project/main_app/templates/source_delete.html index a84de5620..8cd1f066c 100644 --- a/django/cantusdb_project/main_app/templates/source_delete.html +++ b/django/cantusdb_project/main_app/templates/source_delete.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block content %} Delete Source | Cantus Manuscript Database -
+
{% csrf_token %}

diff --git a/django/cantusdb_project/main_app/templates/source_detail.html b/django/cantusdb_project/main_app/templates/source_detail.html index 512955dc5..3d5b8b672 100644 --- a/django/cantusdb_project/main_app/templates/source_detail.html +++ b/django/cantusdb_project/main_app/templates/source_detail.html @@ -58,7 +58,11 @@

{{ source.title }}

Other Editors
{% for editor in source.other_editors.all %} - {{ editor.full_name }}
+ {% if editor.full_name %} + {{ editor.full_name }}
+ {% else %} + User {{ editor.id }}
+ {% endif %} {% endfor %}
{% endif %} @@ -67,7 +71,11 @@

{{ source.title }}

Full Texts Entered by
{% for editor in source.full_text_entered_by.all %} - {{ editor.full_name }}
+ {% if editor.full_name %} + {{ editor.full_name }}
+ {% else %} + User {{ editor.id }}
+ {% endif %} {% endfor %}
{% endif %} @@ -76,7 +84,11 @@

{{ source.title }}

Melodies Entered by
{% for editor in source.melodies_entered_by.all %} - {{ editor.full_name }}
+ {% if editor.full_name %} + {{ editor.full_name }}
+ {% else %} + User {{ editor.id }}
+ {% endif %} {% endfor %}
{% endif %} @@ -245,7 +257,11 @@

{{ source.siglum }}

diff --git a/django/cantusdb_project/main_app/templates/user_detail.html b/django/cantusdb_project/main_app/templates/user_detail.html index d25357c57..be5caaf9b 100644 --- a/django/cantusdb_project/main_app/templates/user_detail.html +++ b/django/cantusdb_project/main_app/templates/user_detail.html @@ -6,7 +6,13 @@ {% include "global_search_bar.html" %} -

{{ user.full_name|default_if_none:"User" }}

+

+ {% if user.full_name %} + {{ user.full_name }} + {% else %} + User {{ user.id }} + {% endif %} +

{% if user.institution %}
Institution
diff --git a/django/cantusdb_project/main_app/templates/user_list.html b/django/cantusdb_project/main_app/templates/user_list.html index 408556e36..c3208ec04 100644 --- a/django/cantusdb_project/main_app/templates/user_list.html +++ b/django/cantusdb_project/main_app/templates/user_list.html @@ -30,7 +30,11 @@

All Users

{% for user in users %} - {{ user.full_name|default:"" }} + {% if user.full_name %} + {{ user.full_name|default:"" }} + {% else %} + User {{ user.id }} + {% endif %} {{ user.institution|default:"" }} {{ user.city|default:"" }} diff --git a/django/cantusdb_project/main_app/tests/make_fakes.py b/django/cantusdb_project/main_app/tests/make_fakes.py index 038e58968..94ada55e4 100644 --- a/django/cantusdb_project/main_app/tests/make_fakes.py +++ b/django/cantusdb_project/main_app/tests/make_fakes.py @@ -17,12 +17,11 @@ from typing import Optional, List +User = get_user_model() + # Max positive integer accepted by Django's positive integer field MAX_SEQUENCE_NUMBER = 2147483647 -# The incipit will be up to 100 characters of the full text -INCIPIT_LENGTH = 100 - # Create a Faker instance with locale set to Latin faker = Faker("la") @@ -146,7 +145,6 @@ def make_fake_chant( feast=None, mode=None, manuscript_full_text_std_spelling=None, - incipit=None, manuscript_full_text_std_proofread=None, manuscript_full_text=None, volpiano=None, @@ -180,8 +178,6 @@ def make_fake_chant( mode = make_random_string(1, "0123456789*?") if manuscript_full_text_std_spelling is None: manuscript_full_text_std_spelling = faker.sentence() - if incipit is None: - incipit = manuscript_full_text_std_spelling[0:INCIPIT_LENGTH] if manuscript_full_text_std_proofread is None: manuscript_full_text_std_proofread = False if manuscript_full_text is None: @@ -214,7 +210,6 @@ def make_fake_chant( ), # chant_range is of the form "1-x-y-4", x, y are volpiano notes addendum=make_random_string(3, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"), manuscript_full_text_std_spelling=manuscript_full_text_std_spelling, - incipit=incipit, manuscript_full_text_std_proofread=manuscript_full_text_std_proofread, manuscript_full_text=manuscript_full_text, manuscript_full_text_proofread=faker.boolean(), @@ -228,6 +223,9 @@ def make_fake_chant( json_info=None, next_chant=next_chant, ) + chant.refresh_from_db() # several fields (e.g., incipit) are calculated automatically + # upon chant save. By refreshing from db before returning, we ensure all the chant's fields + # are up-to-date. For more information, refer to main_app/signals.py return chant @@ -256,9 +254,9 @@ def make_fake_genre(name=None) -> Genre: return genre -def make_fake_user(is_indexer=True) -> get_user_model(): +def make_fake_user(is_indexer=True) -> User: """Generates a fake User object.""" - user = get_user_model().objects.create( + user = User.objects.create( full_name=f"{faker.first_name()} {faker.last_name()}", institution=faker.company(), city=faker.city(), @@ -310,22 +308,17 @@ def make_fake_segment(name: str = None, id: int = None) -> Segment: return segment -def make_fake_sequence( - source=None, title=None, incipit=None, cantus_id=None -) -> Sequence: +def make_fake_sequence(source=None, title=None, cantus_id=None) -> Sequence: """Generates a fake Sequence object.""" if source is None: source = make_fake_source(segment_name="Bower Sequence Database") if title is None: title = faker.sentence() - if incipit is None: - incipit = title[:INCIPIT_LENGTH] if cantus_id is None: cantus_id = make_random_string(6, "0123456789") sequence = Sequence.objects.create( title=title, siglum=make_random_string(6), - incipit=incipit, # folio in the form of two digits and one letter folio=faker.bothify("##?"), s_sequence=make_random_string(2, "0123456789"), @@ -339,6 +332,9 @@ def make_fake_sequence( cantus_id=cantus_id, image_link=faker.image_url(), ) + sequence.refresh_from_db() # several fields (e.g., incipit) are calculated automatically + # upon sequence save. By refreshing from db before returning, we ensure all the sequence's fields + # are up-to-date. For more information, refer to main_app/signals.py return sequence diff --git a/django/cantusdb_project/main_app/tests/test_functions.py b/django/cantusdb_project/main_app/tests/test_functions.py index e898930ab..f2a4b5a2b 100644 --- a/django/cantusdb_project/main_app/tests/test_functions.py +++ b/django/cantusdb_project/main_app/tests/test_functions.py @@ -9,6 +9,7 @@ make_fake_source, ) from main_app.management.commands import update_cached_concordances +from main_app.signals import generate_incipit # run with `python -Wa manage.py test main_app.tests.test_functions` # the -Wa flag tells Python to display deprecation warnings @@ -100,3 +101,20 @@ def test_concordances_values(self): observed_value: Union[str, int, None] = single_concordance[key] with self.subTest(key=key): self.assertEqual(observed_value, value) + + +class IncipitSignalTest(TestCase): + # testing an edge case in generate_incipit, within main_app/signals.py. + # Some other tests involving this function can be found + # in ChantModelTest and SequenceModelTest. + def test_generate_incipit(self): + complete_fulltext: str = "one two three four five six seven" + expected_incipit_1: str = "one two three four five" + observed_incipit_1: str = generate_incipit(complete_fulltext) + with self.subTest(test="full-length fulltext"): + self.assertEqual(observed_incipit_1, expected_incipit_1) + short_fulltext: str = "one*" + expected_incipit_2 = "one*" + observed_incipit_2 = generate_incipit(short_fulltext) + with self.subTest(test="fulltext that's already a short incipit"): + self.assertEqual(observed_incipit_2, expected_incipit_2) diff --git a/django/cantusdb_project/main_app/tests/test_models.py b/django/cantusdb_project/main_app/tests/test_models.py index d1734f635..00c4d0be2 100644 --- a/django/cantusdb_project/main_app/tests/test_models.py +++ b/django/cantusdb_project/main_app/tests/test_models.py @@ -231,6 +231,17 @@ def test_get_next_chant__lacuna(self): self.assertEqual(chant1.get_next_chant(), lacuna) self.assertEqual(lacuna.get_next_chant(), chant3) + def test_incipit_signal(self): + """Test whether a chant's incipit is updated to reflect its fulltext upon save""" + chant: Chant = make_fake_chant() + full_text: str = "Incipit should be five words sheep headphones bongoes" + expected_incipit: str = "Incipit should be five words" + chant.manuscript_full_text_std_spelling = full_text + chant.save() + chant.refresh_from_db() + observed_incipit: str = chant.incipit + self.assertEqual(observed_incipit, expected_incipit) + class FeastModelTest(TestCase): @classmethod @@ -349,6 +360,17 @@ def test_chant_and_sequence_have_same_fields(self): seq_fields = Sequence.get_fields_and_properties() self.assertEqual(chant_fields, seq_fields) + def test_incipit_signal(self): + """Test whether a sequence's incipit is updated to reflect its title upon save""" + sequence: Sequence = make_fake_sequence() + title: str = "Incipit titulus esse debet" + expected_incipit: str = title + sequence.title = title + sequence.save() + sequence.refresh_from_db() + observed_incipit: str = sequence.incipit + self.assertEqual(observed_incipit, expected_incipit) + class SourceModelTest(TestCase): @classmethod diff --git a/django/cantusdb_project/main_app/tests/test_views.py b/django/cantusdb_project/main_app/tests/test_views.py index b2f972536..6860a73ce 100644 --- a/django/cantusdb_project/main_app/tests/test_views.py +++ b/django/cantusdb_project/main_app/tests/test_views.py @@ -827,7 +827,6 @@ def test_chant_with_volpiano_with_no_fulltext(self): chant = make_fake_chant( source=source, volpiano="1---c--g--e---e---d---c---c---f---e---e--d---d---c", - incipit="somebody", ) chant.manuscript_full_text = None chant.manuscript_full_text_std_spelling = None @@ -1083,9 +1082,13 @@ def test_search_bar_search(self): def test_order_by_siglum(self): source_1 = make_fake_source(published=True, siglum="sigl-1") - chant_1 = make_fake_chant(incipit="thing 1", source=source_1) + chant_1 = make_fake_chant( + manuscript_full_text_std_spelling="thing 1", source=source_1 + ) source_2 = make_fake_source(published=True, siglum="sigl-2") - chant_2 = make_fake_chant(incipit="thing 2", source=source_2) + chant_2 = make_fake_chant( + manuscript_full_text_std_spelling="thing 2", source=source_2 + ) search_term = "thing" @@ -1121,8 +1124,12 @@ def test_order_by_siglum(self): def test_order_by_incipit(self): source = make_fake_source(published=True) - chant_1 = make_fake_chant(source=source, incipit="higgledy") - chant_2 = make_fake_chant(source=source, incipit="piggledy") + chant_1 = make_fake_chant( + source=source, manuscript_full_text_std_spelling="higgledy" + ) + chant_2 = make_fake_chant( + source=source, manuscript_full_text_std_spelling="piggledy" + ) search_term = "iggl" @@ -1161,8 +1168,12 @@ def test_order_by_office(self): office_1 = make_fake_office() office_2 = make_fake_office() assert office_1.id < office_2.id - chant_1 = make_fake_chant(office=office_1, incipit="hocus") - chant_2 = make_fake_chant(office=office_2, incipit="pocus") + chant_1 = make_fake_chant( + office=office_1, manuscript_full_text_std_spelling="hocus" + ) + chant_2 = make_fake_chant( + office=office_2, manuscript_full_text_std_spelling="pocus" + ) search_term = "ocu" @@ -1201,8 +1212,12 @@ def test_order_by_genre(self): genre_1 = make_fake_genre() genre_2 = make_fake_genre() assert genre_1.id < genre_2.id - chant_1 = make_fake_chant(genre=genre_1, incipit="hocus") - chant_2 = make_fake_chant(genre=genre_2, incipit="pocus") + chant_1 = make_fake_chant( + genre=genre_1, manuscript_full_text_std_spelling="hocus" + ) + chant_2 = make_fake_chant( + genre=genre_2, manuscript_full_text_std_spelling="focus" + ) search_term = "ocu" @@ -1237,8 +1252,12 @@ def test_order_by_genre(self): self.assertEqual(last_result_incipit, chant_1.incipit) def test_order_by_cantus_id(self): - chant_1 = make_fake_chant(incipit="isaac", cantus_id="121393") - chant_2 = make_fake_chant(incipit="baal", cantus_id="196418") + chant_1 = make_fake_chant( + manuscript_full_text_std_spelling="isaac", cantus_id="121393" + ) + chant_2 = make_fake_chant( + manuscript_full_text_std_spelling="baal", cantus_id="196418" + ) search_term = "aa" @@ -1274,11 +1293,11 @@ def test_order_by_cantus_id(self): def test_order_by_mode(self): chant_1 = make_fake_chant( - incipit="For first he looks upon his forepaws to see if they are clean", + manuscript_full_text_std_spelling="For first he looks upon his forepaws to see if they are clean", mode="1", ) chant_2 = make_fake_chant( - incipit="For secondly he kicks up behind to clear away there", + manuscript_full_text_std_spelling="For secondly he kicks up behind to clear away there", mode="2", ) @@ -1316,16 +1335,16 @@ def test_order_by_mode(self): def test_order_by_ms_fulltext(self): chant_1 = make_fake_chant( - incipit="this is a chant with a MS spelling fulltext", manuscript_full_text="this is a chant with a MS spelling fylltexte", + manuscript_full_text_std_spelling="this is a chant with a MS spelling fulltext", ) chant_2 = make_fake_chant( - incipit="this is a chant without", + manuscript_full_text_std_spelling="this will become a chant without a MS spelling fulltext", ) chant_2.manuscript_full_text = None chant_2.save() - search_term = "s is a ch" + search_term = "a chant wit" response_ascending = self.client.get( reverse("chant-search"), @@ -1359,11 +1378,11 @@ def test_order_by_ms_fulltext(self): def test_order_by_volpiano(self): chant_1 = make_fake_chant( - incipit="this is a chant with volpiano", + manuscript_full_text_std_spelling="this is a chant with volpiano", volpiano="1---d---d---a--a---a---e--f--e---d---4", ) chant_2 = make_fake_chant( - incipit="this is a chant about parsley", + manuscript_full_text_std_spelling="this is a chant about parsley", ) chant_2.volpiano = None chant_2.save() @@ -1402,11 +1421,11 @@ def test_order_by_volpiano(self): def test_order_by_image_link(self): chant_1 = make_fake_chant( - incipit="this is a chant with a link", + manuscript_full_text_std_spelling="this is a chant with a link", image_link="https://www.youtube.com/watch?v=dQw4w9WgXcQ", ) chant_2 = make_fake_chant( - incipit="this is a chant without", + manuscript_full_text_std_spelling="this is a chant without", ) chant_2.image_link = None chant_2.save() @@ -1446,12 +1465,11 @@ def test_order_by_image_link(self): def test_column_header_links(self): # these are the 9 column headers users can order by: siglum = "glum-01" - incipit = "so it begins" + fulltext = "so it begins" office = make_fake_office() genre = make_fake_genre() cantus_id = make_random_string(6, "0123456789") mode = make_random_string(1, "0123456789*?") - ms_ft = faker.sentence() mel = make_fake_volpiano() image = faker.image_url() @@ -1461,12 +1479,11 @@ def test_column_header_links(self): feast = make_fake_feast() position = make_random_string(1) chant = make_fake_chant( - incipit=incipit, + manuscript_full_text_std_spelling=fulltext, office=office, genre=genre, cantus_id=cantus_id, mode=mode, - manuscript_full_text_std_spelling=ms_ft, volpiano=mel, image_link=image, source=source, @@ -1968,30 +1985,36 @@ def test_keyword_search_searching_all_fields(self): includes_search_term = "brevity is the soul of wit" doesnt_include_search_term = "longevity is the soul of wit" source = make_fake_source() - chant_incipit = make_fake_chant( - source=source, - incipit=includes_search_term, # <== - manuscript_full_text=doesnt_include_search_term, - manuscript_full_text_std_spelling=doesnt_include_search_term, - ) + chant_ms_spelling = make_fake_chant( source=source, - incipit=doesnt_include_search_term, - manuscript_full_text=includes_search_term, # <== + manuscript_full_text=includes_search_term, # <== includes_search_term manuscript_full_text_std_spelling=doesnt_include_search_term, ) + chant_std_spelling = make_fake_chant( source=source, - incipit=doesnt_include_search_term, manuscript_full_text=doesnt_include_search_term, manuscript_full_text_std_spelling=includes_search_term, # <== ) + + # some chants inherited from OldCantus have an incipit but no full-text - + # we need to ensure these chants appear in the results + chant_incipit = make_fake_chant( + source=source, + ) + Chant.objects.filter(id=chant_incipit.id).update( + incipit=includes_search_term, # <== + manuscript_full_text=None, + manuscript_full_text_std_spelling=None, + ) + chant_without_search_term = make_fake_chant( source=source, - incipit=doesnt_include_search_term, manuscript_full_text=doesnt_include_search_term, manuscript_full_text_std_spelling=doesnt_include_search_term, ) + response_starts_with = self.client.get( reverse("chant-search-ms", args=[source.id]), {"keyword": search_term, "op": "starts_with"}, @@ -2005,8 +2028,12 @@ def test_keyword_search_searching_all_fields(self): def test_order_by_incipit(self): source = make_fake_source(published=True) - chant_1 = make_fake_chant(source=source, incipit="higgledy") - chant_2 = make_fake_chant(source=source, incipit="piggledy") + chant_1 = make_fake_chant( + source=source, manuscript_full_text_std_spelling="higgledy" + ) + chant_2 = make_fake_chant( + source=source, manuscript_full_text_std_spelling="piggledy" + ) search_term = "iggl" @@ -2046,8 +2073,12 @@ def test_order_by_office(self): office_1 = make_fake_office() office_2 = make_fake_office() assert office_1.id < office_2.id - chant_1 = make_fake_chant(office=office_1, incipit="hocus", source=source) - chant_2 = make_fake_chant(office=office_2, incipit="pocus", source=source) + chant_1 = make_fake_chant( + office=office_1, manuscript_full_text_std_spelling="hocus", source=source + ) + chant_2 = make_fake_chant( + office=office_2, manuscript_full_text_std_spelling="pocus", source=source + ) search_term = "ocu" @@ -2087,8 +2118,12 @@ def test_order_by_genre(self): genre_1 = make_fake_genre() genre_2 = make_fake_genre() assert genre_1.id < genre_2.id - chant_1 = make_fake_chant(genre=genre_1, incipit="hocus", source=source) - chant_2 = make_fake_chant(genre=genre_2, incipit="pocus", source=source) + chant_1 = make_fake_chant( + genre=genre_1, manuscript_full_text_std_spelling="hocus", source=source + ) + chant_2 = make_fake_chant( + genre=genre_2, manuscript_full_text_std_spelling="pocus", source=source + ) search_term = "ocu" @@ -2124,8 +2159,12 @@ def test_order_by_genre(self): def test_order_by_cantus_id(self): source = make_fake_source() - chant_1 = make_fake_chant(incipit="isaac", cantus_id="121393", source=source) - chant_2 = make_fake_chant(incipit="baal", cantus_id="196418", source=source) + chant_1 = make_fake_chant( + manuscript_full_text_std_spelling="isaac", cantus_id="121393", source=source + ) + chant_2 = make_fake_chant( + manuscript_full_text_std_spelling="baal", cantus_id="196418", source=source + ) search_term = "aa" @@ -2162,12 +2201,12 @@ def test_order_by_cantus_id(self): def test_order_by_mode(self): source = make_fake_source() chant_1 = make_fake_chant( - incipit="For thirdly he works it upon stretch with the forepaws extended", + manuscript_full_text_std_spelling="For thirdly he works it upon stretch with the forepaws extended", mode="1", source=source, ) chant_2 = make_fake_chant( - incipit="For fourthly he sharpens his paws by wood", + manuscript_full_text_std_spelling="For fourthly he sharpens his paws by wood", mode="2", source=source, ) @@ -2207,12 +2246,12 @@ def test_order_by_mode(self): def test_order_by_ms_fulltext(self): source = make_fake_source() chant_1 = make_fake_chant( - incipit="this is a chant with a MS spelling fulltext", + manuscript_full_text_std_spelling="this is a chant with a MS spelling fulltext", manuscript_full_text="this is a chant with a MS spelling fylltexte", source=source, ) chant_2 = make_fake_chant( - incipit="this is a chant without", + manuscript_full_text_std_spelling="this is a chant without", source=source, ) chant_2.manuscript_full_text = None @@ -2254,12 +2293,12 @@ def test_order_by_volpiano(self): source = make_fake_source() chant_1 = make_fake_chant( source=source, - incipit="this is a chant with volpiano", + manuscript_full_text_std_spelling="this is a chant with volpiano", volpiano="1---d---d---a--a---a---e--f--e---d---4", ) chant_2 = make_fake_chant( source=source, - incipit="this is a chant about parsley", + manuscript_full_text_std_spelling="this is a chant about parsley", ) chant_2.volpiano = None chant_2.save() @@ -2300,12 +2339,12 @@ def test_order_by_image_link(self): source = make_fake_source() chant_1 = make_fake_chant( source=source, - incipit="this is a chant with a link", + manuscript_full_text_std_spelling="this is a chant with a link", image_link="https://www.youtube.com/watch?v=dQw4w9WgXcQ", ) chant_2 = make_fake_chant( source=source, - incipit="this is a chant without", + manuscript_full_text_std_spelling="this is a chant without", ) chant_2.image_link = None chant_2.save() @@ -2345,12 +2384,12 @@ def test_order_by_image_link(self): def test_column_header_links(self): # these are the 9 column headers users can order by: siglum = "glum-01" - incipit = "so it begins" + full_text = "this is a full text that begins with the search term" + search_term = "this is a fu" office = make_fake_office() genre = make_fake_genre() cantus_id = make_random_string(6, "0123456789") mode = make_random_string(1, "0123456789*?") - ms_ft = faker.sentence() mel = make_fake_volpiano() image = faker.image_url() @@ -2360,20 +2399,17 @@ def test_column_header_links(self): feast = make_fake_feast() position = make_random_string(1) chant = make_fake_chant( - incipit=incipit, office=office, genre=genre, cantus_id=cantus_id, mode=mode, - manuscript_full_text_std_spelling=ms_ft, + manuscript_full_text_std_spelling=full_text, volpiano=mel, image_link=image, source=source, feast=feast, position=position, ) - search_term = "so it be" - response_1 = self.client.get( reverse("chant-search-ms", args=[source.id]), { @@ -3012,6 +3048,8 @@ def test_volpiano_signal(self): folio="001r", c_sequence=2, ) + expected_volpiano: str = "abacadaeafagahaja" + expected_intervals: str = "1-12-23-34-45-56-67-78-8" self.client.post( reverse("source-edit-chants", args=[source.id]), { @@ -3022,15 +3060,13 @@ def test_volpiano_signal(self): }, ) chant_2 = Chant.objects.get(manuscript_full_text_std_spelling="resonare foobaz") - self.assertEqual(chant_2.volpiano, "abacadaeafagahaja") - self.assertEqual(chant_2.volpiano_intervals, "1-12-23-34-45-56-67-78-8") + self.assertEqual(chant_2.volpiano, expected_volpiano) + self.assertEqual(chant_2.volpiano_intervals, expected_intervals) def test_chant_with_volpiano_with_no_fulltext(self): # in the past, a Chant Edit page will error rather than loading properly when the chant has volpiano but no fulltext source = make_fake_source() - chant = make_fake_chant( - source=source, volpiano="1---f--e---f--d---e--c---d--d", incipit="dies irae" - ) + chant = make_fake_chant(source=source, volpiano="1---f--e---f--d---e--c---d--d") chant.manuscript_full_text = None chant.manuscript_full_text_std_spelling = None chant.save() @@ -3114,7 +3150,7 @@ def test_view_url_and_templates(self): def test_edit_syllabification(self): chant = make_fake_chant(manuscript_syllabized_full_text="lorem ipsum") - self.assertIs(chant.manuscript_syllabized_full_text, "lorem ipsum") + self.assertEqual(chant.manuscript_syllabized_full_text, "lorem ipsum") response = self.client.post( f"/edit-syllabification/{chant.id}", {"manuscript_syllabized_full_text": "lo-rem ip-sum"}, @@ -3740,7 +3776,7 @@ def test_url_and_templates(self): self.assertTemplateUsed(response, "404.html") def test_update_sequence(self): - sequence = make_fake_sequence(incipit="test_update_sequence") + sequence = make_fake_sequence() sequence_id = str(sequence.id) response = self.client.post( reverse("sequence-edit", args=[sequence_id]), @@ -5068,6 +5104,7 @@ def test_structure(self): def test_values(self): chant = make_fake_chant(cantus_id="100000") + expected_values = { "siglum": chant.source.siglum, "srclink": f"http://testserver/source/{chant.source.id}", @@ -5459,7 +5496,7 @@ def test_content_overview_view_selected_model(self): def test_source_selected_model(self): source = make_fake_source(title="Test Source") - chant = make_fake_chant(incipit="Test Chant") + chant = make_fake_chant() response = self.client.get(reverse("content-overview"), {"model": "sources"}) self.assertContains(response, f"Sources", html=True) self.assertContains( @@ -5578,15 +5615,17 @@ def test_response(self): def test_incipit_search(self): unremarkable_chant = make_fake_chant( - incipit=( - "The incipit contains no " + manuscript_full_text_std_spelling=( + "The fulltext contains no " "numbers no asterisks and no punctuation " "and is thus completely normal" ) ) - chant_with_asterisk = make_fake_chant(incipit="few words*") + chant_with_asterisk = make_fake_chant( + manuscript_full_text_std_spelling="few words*" + ) - istartswith_search_term = "the incipit" + istartswith_search_term = "the fulltext" istartswith_response = self.client.get( reverse("ajax-search-bar", args=[istartswith_search_term]) ) @@ -5622,11 +5661,11 @@ def test_incipit_search(self): def test_cantus_id_search(self): chant_with_normal_cantus_id = make_fake_chant( cantus_id="012345", - incipit="This incipit contains no numerals", + manuscript_full_text_std_spelling="This fulltext contains no numerals", ) chant_with_numerals_in_incipit = make_fake_chant( cantus_id="123456", - incipit="0 me! 0 my! This is unexpected!", + manuscript_full_text_std_spelling="0 me! 0 my! This is unexpected!", ) # for search terms that contain numerals, we should only return