From e0eb15681e2a86b50a4db607abfb70d1c482108e Mon Sep 17 00:00:00 2001 From: Jacob deGroot-Maggetti <58090591+jacobdgm@users.noreply.github.com> Date: Fri, 29 Mar 2024 15:54:37 +0000 Subject: [PATCH 01/12] Bump black from 21.10b0 to 24.3.0 --- django/cantusdb_project/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/cantusdb_project/requirements.txt b/django/cantusdb_project/requirements.txt index 889947a2c..8fa5376f3 100644 --- a/django/cantusdb_project/requirements.txt +++ b/django/cantusdb_project/requirements.txt @@ -3,7 +3,7 @@ asgiref==3.6.0 astroid==2.4.2 attrs==19.3.0 backports.zoneinfo==0.2.1 -black==21.10b0 +black==24.3.0 certifi==2023.7.22 chardet==3.0.4 charset-normalizer==2.0.12 From 89f1e3afbe3f709d7886db49fb006f3706a37619 Mon Sep 17 00:00:00 2001 From: Jacob deGroot-Maggetti <58090591+jacobdgm@users.noreply.github.com> Date: Fri, 29 Mar 2024 15:56:13 +0000 Subject: [PATCH 02/12] Bump typing-extensions from 3.10.0.0 to 4.0.1 --- django/cantusdb_project/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/cantusdb_project/requirements.txt b/django/cantusdb_project/requirements.txt index 8fa5376f3..204b55be9 100644 --- a/django/cantusdb_project/requirements.txt +++ b/django/cantusdb_project/requirements.txt @@ -36,7 +36,7 @@ sqlparse==0.4.4 text-unidecode==1.3 toml==0.10.1 typed-ast==1.4.1 -typing-extensions==3.10.0.0 +typing-extensions==4.0.1 ujson==5.9.0 urllib3==1.26.18 volpiano-display-utilities @ git+https://github.com/DDMAL/volpiano-display-utilities.git@v1.1.2 From 7ff344cb589b9aa5f17b229eb063b35596786da3 Mon Sep 17 00:00:00 2001 From: Jacob deGroot-Maggetti <58090591+jacobdgm@users.noreply.github.com> Date: Fri, 29 Mar 2024 15:57:15 +0000 Subject: [PATCH 03/12] Bump click from 7.1.2 to 8.0.0 --- django/cantusdb_project/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/cantusdb_project/requirements.txt b/django/cantusdb_project/requirements.txt index 204b55be9..270ffceac 100644 --- a/django/cantusdb_project/requirements.txt +++ b/django/cantusdb_project/requirements.txt @@ -7,7 +7,7 @@ black==24.3.0 certifi==2023.7.22 chardet==3.0.4 charset-normalizer==2.0.12 -click==7.1.2 +click==8.0.0 coverage==5.3.1 Django==4.2.11 django-autocomplete-light==3.9.4 From d9e2841e523b0305ef71bc6cb949e05d81c72bd8 Mon Sep 17 00:00:00 2001 From: Jacob deGroot-Maggetti <58090591+jacobdgm@users.noreply.github.com> Date: Fri, 29 Mar 2024 15:57:44 +0000 Subject: [PATCH 04/12] Reformat several files to comply with updated Black version --- django/cantusdb_project/cantusdb/urls.py | 1 + django/cantusdb_project/main_app/models/base_model.py | 1 + django/cantusdb_project/main_app/tests/make_fakes.py | 1 + django/cantusdb_project/main_app/tests/test_views.py | 8 +++++--- scripts/parse_link_checker_output.py | 1 + 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/django/cantusdb_project/cantusdb/urls.py b/django/cantusdb_project/cantusdb/urls.py index e39f736f6..0179b5e5d 100644 --- a/django/cantusdb_project/cantusdb/urls.py +++ b/django/cantusdb_project/cantusdb/urls.py @@ -13,6 +13,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.contrib import admin from django.urls import path from django.urls import include diff --git a/django/cantusdb_project/main_app/models/base_model.py b/django/cantusdb_project/main_app/models/base_model.py index dca07ac08..f64b44ff7 100644 --- a/django/cantusdb_project/main_app/models/base_model.py +++ b/django/cantusdb_project/main_app/models/base_model.py @@ -1,4 +1,5 @@ """Defines a BaseModel to be extended by all other models""" + from typing import List from django.db import models from django.urls import reverse diff --git a/django/cantusdb_project/main_app/tests/make_fakes.py b/django/cantusdb_project/main_app/tests/make_fakes.py index 94ada55e4..47177eb57 100644 --- a/django/cantusdb_project/main_app/tests/make_fakes.py +++ b/django/cantusdb_project/main_app/tests/make_fakes.py @@ -1,4 +1,5 @@ """Functions to make fake objects to be used for testing""" + import random from faker import Faker diff --git a/django/cantusdb_project/main_app/tests/test_views.py b/django/cantusdb_project/main_app/tests/test_views.py index b4f7f3af8..4ab4d0ac8 100644 --- a/django/cantusdb_project/main_app/tests/test_views.py +++ b/django/cantusdb_project/main_app/tests/test_views.py @@ -2886,7 +2886,7 @@ def test_volpiano_signal(self): "c_sequence": "1", # liquescents, to be converted to lowercase # vv v v v v v v - "volpiano": "9abcdefg)A-B1C2D3E4F5G67?. yiz" + "volpiano": "9abcdefg)A-B1C2D3E4F5G67?. yiz", # ^ ^ ^ ^ ^ ^ ^^^^^^^^ # clefs, accidentals, etc., to be deleted }, @@ -3033,7 +3033,7 @@ def test_volpiano_signal(self): "c_sequence": "1", # liquescents, to be converted to lowercase # vv v v v v v v - "volpiano": "9abcdefg)A-B1C2D3E4F5G67?. yiz" + "volpiano": "9abcdefg)A-B1C2D3E4F5G67?. yiz", # ^ ^ ^ ^ ^ ^ ^^^^^^^^ # clefs, accidentals, etc., to be deleted }, @@ -4510,7 +4510,9 @@ def test_dd_column(self): response = self.client.get(reverse("source-inventory", args=[source.id])) html: str = str(response.content) self.assertIn(diff_id, html) - expected_html_substring: str = f'' + expected_html_substring: str = ( + f'' + ) self.assertIn(expected_html_substring, html) def test_redirect_with_source_parameter(self): diff --git a/scripts/parse_link_checker_output.py b/scripts/parse_link_checker_output.py index 172f84684..178d91109 100644 --- a/scripts/parse_link_checker_output.py +++ b/scripts/parse_link_checker_output.py @@ -1,4 +1,5 @@ """Modules""" + import json import sys from pathlib import Path From 9dbb859fcdf53d8b9980a43e0bc2546954d60313 Mon Sep 17 00:00:00 2001 From: Jacob deGroot-Maggetti <58090591+jacobdgm@users.noreply.github.com> Date: Tue, 2 Apr 2024 13:32:46 +0000 Subject: [PATCH 05/12] Remove ajax_concordance_list view --- django/cantusdb_project/main_app/urls.py | 5 -- .../cantusdb_project/main_app/views/views.py | 55 ------------------- 2 files changed, 60 deletions(-) diff --git a/django/cantusdb_project/main_app/urls.py b/django/cantusdb_project/main_app/urls.py index 7c1ed6039..7661db2f7 100644 --- a/django/cantusdb_project/main_app/urls.py +++ b/django/cantusdb_project/main_app/urls.py @@ -391,11 +391,6 @@ views.csv_export_redirect_from_old_path, name="csv-export-old-path", ), - path( - "ajax/concordance/", - views.ajax_concordance_list, - name="ajax-concordance", - ), # content overview (for project managers) path( "content-overview/", diff --git a/django/cantusdb_project/main_app/views/views.py b/django/cantusdb_project/main_app/views/views.py index 54a40fbcc..1e22ee090 100644 --- a/django/cantusdb_project/main_app/views/views.py +++ b/django/cantusdb_project/main_app/views/views.py @@ -69,61 +69,6 @@ def items_count(request): return render(request, "items_count.html", context) -def ajax_concordance_list(request, cantus_id): - """ - Function-based view responding to the AJAX call for concordance list on the chant detail page, - accessed with ``chants/``, click on "Display concordances of this chant" - - Args: - cantus_id (str): The Cantus ID of the requested concordances group - - Returns: - JsonResponse: A response to the AJAX call, to be unpacked by the frontend js code - """ - chants = Chant.objects.filter(cantus_id=cantus_id) - seqs = Sequence.objects.filter(cantus_id=cantus_id) - - display_unpublished = request.user.is_authenticated - if not display_unpublished: - chants = chants.filter(source__published=True) - seqs = seqs.filter(source__published=True) - - if seqs: - chants = chants.union(seqs).order_by("siglum", "folio") - else: - chants = chants.order_by("siglum", "folio") - # queryset(list of dictionaries) - concordance_values = chants.values( - "siglum", - "folio", - "incipit", - "office__name", - "genre__name", - "position", - "feast__name", - "mode", - "image_link", - ) - - concordances = list(concordance_values) - for i, concordance in enumerate(concordances): - # some chants do not have a source - # for those chants, do not return source link - if chants[i].source: - concordance["source_link"] = chants[i].source.get_absolute_url() - if chants[i].search_vector: - concordance["chant_link"] = chants[i].get_absolute_url() - else: - concordance["chant_link"] = reverse("sequence-detail", args=[chants[i].id]) - concordance["db"] = "CD" - - concordance_count = len(concordances) - return JsonResponse( - {"concordances": concordances, "concordance_count": concordance_count}, - safe=True, - ) - - def ajax_melody_list(request, cantus_id) -> JsonResponse: """ Function-based view responding to the AJAX call for melody list on the chant detail page, From 36d35537bb499c8019ac37ea4afab1f708263aff Mon Sep 17 00:00:00 2001 From: Jacob deGroot-Maggetti <58090591+jacobdgm@users.noreply.github.com> Date: Tue, 2 Apr 2024 14:51:15 +0000 Subject: [PATCH 06/12] Refactor CsvExportTest mostly to use subTests --- .../main_app/tests/test_views.py | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/django/cantusdb_project/main_app/tests/test_views.py b/django/cantusdb_project/main_app/tests/test_views.py index 4ab4d0ac8..1438e4624 100644 --- a/django/cantusdb_project/main_app/tests/test_views.py +++ b/django/cantusdb_project/main_app/tests/test_views.py @@ -5215,11 +5215,15 @@ def test_content(self): "node_id", ] for t in expected_column_titles: - self.assertIn(t, header) - - self.assertEqual(len(rows), NUM_CHANTS) - for row in rows: - self.assertEqual(len(header), len(row)) + with self.subTest(expected_column=t): + self.assertIn(t, header) + with self.subTest(subtest="ensure a row exists for each chant"): + self.assertEqual(len(rows), NUM_CHANTS) + with self.subTest( + subtest="ensure all rows have the same number of columns as the header" + ): + for row in rows: + self.assertEqual(len(header), len(row)) def test_published_vs_unpublished(self): published_source = make_fake_source(published=True) @@ -5245,12 +5249,18 @@ def test_csv_export_on_source_with_sequences(self): split_content = list(csv.reader(content.splitlines(), delimiter=",")) header, rows = split_content[0], split_content[1:] - self.assertEqual(len(rows), NUM_SEQUENCES) - for row in rows: - self.assertEqual(len(header), len(row)) - self.assertNotEqual( - row[3], "" - ) # ensure that the .s_sequence field is being written to the "sequence" column + with self.subTest(subtest="ensure a row exists for each sequence"): + self.assertEqual(len(rows), NUM_SEQUENCES) + with self.subTest( + subtest="ensure all rows have the same number of columns as the header" + ): + for row in rows: + self.assertEqual(len(header), len(row)) + with self.subTest( + subtest="ensure .s_sequence field is being written to the 'sequence' column" + ): + for row in rows: + self.assertNotEqual(row[3], "") class ChangePasswordViewTest(TestCase): From 391fa11f4bfc5b4f16a00847b97c89e9b812f395 Mon Sep 17 00:00:00 2001 From: Jacob deGroot-Maggetti <58090591+jacobdgm@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:00:28 +0000 Subject: [PATCH 07/12] Update CsvExportTest.test_content to add subtest checking items' sigla --- .../main_app/tests/test_views.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/django/cantusdb_project/main_app/tests/test_views.py b/django/cantusdb_project/main_app/tests/test_views.py index 1438e4624..8316f6aec 100644 --- a/django/cantusdb_project/main_app/tests/test_views.py +++ b/django/cantusdb_project/main_app/tests/test_views.py @@ -5182,9 +5182,16 @@ def test_url(self): def test_content(self): NUM_CHANTS = 5 - source = make_fake_source(published=True) + source_siglum = "SourceSiglum" + chant_siglum = "ChantSiglum" # OldCantus chants/sequences had a "siglum" + # field, which would sometimes get out of date when the chant's source's siglum + # was updated. We keep the chant siglum field around to ensure no data is + # inadvertently lost, but we need to ensure it is never displayed publicly. + source = make_fake_source(published=True, siglum=source_siglum) for _ in range(NUM_CHANTS): - make_fake_chant(source=source) + chant = make_fake_chant(source=source) + chant.siglum = chant_siglum + chant.save() response = self.client.get(reverse("csv-export", args=[source.id])) content = response.content.decode("utf-8") split_content = list(csv.reader(content.splitlines(), delimiter=",")) @@ -5224,6 +5231,12 @@ def test_content(self): ): for row in rows: self.assertEqual(len(header), len(row)) + with self.subTest( + "ensure we only ever display chants' sources' sigla, and never the " + "value stored in chants' siglum fields" + ): + for row in rows: + self.assertEqual(row[0], source_siglum) def test_published_vs_unpublished(self): published_source = make_fake_source(published=True) From 51d82ddeaa1584b12a68b00cc5bdf65ec50337a6 Mon Sep 17 00:00:00 2001 From: Jacob deGroot-Maggetti <58090591+jacobdgm@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:03:36 +0000 Subject: [PATCH 08/12] Update csv_export view to ensure chants' sigla are never displayed --- django/cantusdb_project/main_app/views/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/django/cantusdb_project/main_app/views/views.py b/django/cantusdb_project/main_app/views/views.py index 54a40fbcc..34865ab11 100644 --- a/django/cantusdb_project/main_app/views/views.py +++ b/django/cantusdb_project/main_app/views/views.py @@ -236,6 +236,7 @@ def csv_export(request, source_id): ] ) for entry in entries: + siglum = entry.source.siglum if entry.source else "" feast = entry.feast.name if entry.feast else "" office = entry.office.name if entry.office else "" genre = entry.genre.name if entry.genre else "" @@ -243,7 +244,7 @@ def csv_export(request, source_id): writer.writerow( [ - entry.siglum, + siglum, entry.marginalia, entry.folio, # if entry has a c_sequence, it's a Chant. If it doesn't, it's a Sequence, so write its s_sequence From 719b91723582a8bb35806ad543129ffc691879ad Mon Sep 17 00:00:00 2001 From: Jacob deGroot-Maggetti <58090591+jacobdgm@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:42:16 +0000 Subject: [PATCH 09/12] Create ChantCreateViewTest.test_initial_values with passing feast subtest and not-yet-passing office subtest --- .../main_app/tests/test_views.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/django/cantusdb_project/main_app/tests/test_views.py b/django/cantusdb_project/main_app/tests/test_views.py index 4ab4d0ac8..522e9cf29 100644 --- a/django/cantusdb_project/main_app/tests/test_views.py +++ b/django/cantusdb_project/main_app/tests/test_views.py @@ -2909,6 +2909,32 @@ def test_volpiano_signal(self): self.assertEqual(chant_2.volpiano, "abacadaeafagahaja") self.assertEqual(chant_2.volpiano_intervals, "1-12-23-34-45-56-67-78-8") + def test_initial_values(self): + source: Source = make_fake_source() + feast: Feast = make_fake_feast() + office: Office = make_fake_office() + # create a chant with a known feast and office + self.client.post( + reverse("chant-create", args=[source.id]), + { + "manuscript_full_text_std_spelling": "this is a bog standard manuscript spelling textful", + "folio": "001r", + "c_sequence": "1", + "feast": feast.id, + "office": office.id, + }, + ) + # when we request the page, the same feast and office should be preselected + request = self.client.get( + reverse("chant-create", args=[source.id]), + ) + observed_initial_feast: int = request.context["form"].initial["feast"] + observed_intitial_office: int = request.context["form"].initial["office"] + with self.subTest(subtest="test initial value of feast feild"): + self.assertEqual(observed_initial_feast, feast.id) + with self.subTest(subtest="test initial value of office field"): + self.assertEqual(observed_intitial_office, office.id) + class ChantDeleteViewTest(TestCase): @classmethod From c16bcb2cfdab15699714feba7c149957374ff014 Mon Sep 17 00:00:00 2001 From: Jacob deGroot-Maggetti <58090591+jacobdgm@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:43:40 +0000 Subject: [PATCH 10/12] Chant Create: ensure office form field is prepopulated with the same office as the most recently created chant --- django/cantusdb_project/main_app/views/chant.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/django/cantusdb_project/main_app/views/chant.py b/django/cantusdb_project/main_app/views/chant.py index bd17143fc..4d7324ea3 100644 --- a/django/cantusdb_project/main_app/views/chant.py +++ b/django/cantusdb_project/main_app/views/chant.py @@ -805,6 +805,7 @@ def get_initial(self): } latest_folio = latest_chant.folio if latest_chant.folio else "001r" latest_feast = latest_chant.feast.id if latest_chant.feast else "" + latest_office = latest_chant.office.id if latest_chant.office else "" latest_seq = ( latest_chant.c_sequence if latest_chant.c_sequence is not None else 0 ) @@ -812,6 +813,7 @@ def get_initial(self): return { "folio": latest_folio, "feast": latest_feast, + "office": latest_office, "c_sequence": latest_seq + 1, "image_link": latest_image, } From 403ad5f0c0359d2036e9a578d2cf51d4bb2fc09c Mon Sep 17 00:00:00 2001 From: Jacob deGroot-Maggetti <58090591+jacobdgm@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:02:24 +0000 Subject: [PATCH 11/12] Optimize csv_export view to check source siglum once per source, rather than once per chant --- django/cantusdb_project/main_app/views/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/cantusdb_project/main_app/views/views.py b/django/cantusdb_project/main_app/views/views.py index 34865ab11..5187b15e7 100644 --- a/django/cantusdb_project/main_app/views/views.py +++ b/django/cantusdb_project/main_app/views/views.py @@ -235,8 +235,8 @@ def csv_export(request, source_id): "node_id", ] ) + siglum = source.siglum for entry in entries: - siglum = entry.source.siglum if entry.source else "" feast = entry.feast.name if entry.feast else "" office = entry.office.name if entry.office else "" genre = entry.genre.name if entry.genre else "" From 7aba709e0669b8f62a3cb3c843f256b60613f9e7 Mon Sep 17 00:00:00 2001 From: Jacob deGroot-Maggetti <58090591+jacobdgm@users.noreply.github.com> Date: Thu, 4 Apr 2024 13:26:39 +0000 Subject: [PATCH 12/12] Expand ChantCreateViewTest.test_initial_values adding subtests for folio, sequence, and image link --- .../main_app/tests/test_views.py | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/django/cantusdb_project/main_app/tests/test_views.py b/django/cantusdb_project/main_app/tests/test_views.py index 522e9cf29..b2c8e28ad 100644 --- a/django/cantusdb_project/main_app/tests/test_views.py +++ b/django/cantusdb_project/main_app/tests/test_views.py @@ -2910,30 +2910,50 @@ def test_volpiano_signal(self): self.assertEqual(chant_2.volpiano_intervals, "1-12-23-34-45-56-67-78-8") def test_initial_values(self): + # create a chant with a known folio, feast, office, c_sequence and image_link source: Source = make_fake_source() + folio: str = "001r" + sequence: int = 1 feast: Feast = make_fake_feast() office: Office = make_fake_office() - # create a chant with a known feast and office + image_link: str = "https://www.youtube.com/watch?v=9bZkp7q19f0" self.client.post( reverse("chant-create", args=[source.id]), { - "manuscript_full_text_std_spelling": "this is a bog standard manuscript spelling textful", - "folio": "001r", - "c_sequence": "1", + "manuscript_full_text_std_spelling": "this is a bog standard manuscript textful spelling", + "folio": folio, + "c_sequence": str(sequence), "feast": feast.id, "office": office.id, + "image_link": image_link, }, ) - # when we request the page, the same feast and office should be preselected - request = self.client.get( + + # when we request the Chant Create page, the same folio, feast, office and image_link should + # be preselected, and c_sequence should be incremented by 1. + response = self.client.get( reverse("chant-create", args=[source.id]), ) - observed_initial_feast: int = request.context["form"].initial["feast"] - observed_intitial_office: int = request.context["form"].initial["office"] + + observed_initial_folio: int = response.context["form"].initial["folio"] + with self.subTest(subtest="test initial value of folio field"): + self.assertEqual(observed_initial_folio, folio) + + observed_initial_feast: int = response.context["form"].initial["feast"] with self.subTest(subtest="test initial value of feast feild"): self.assertEqual(observed_initial_feast, feast.id) + + observed_initial_office: int = response.context["form"].initial["office"] with self.subTest(subtest="test initial value of office field"): - self.assertEqual(observed_intitial_office, office.id) + self.assertEqual(observed_initial_office, office.id) + + observed_initial_sequence: int = response.context["form"].initial["c_sequence"] + with self.subTest(subtest="test initial value of c_sequence field"): + self.assertEqual(observed_initial_sequence, sequence + 1) + + observed_initial_image: int = response.context["form"].initial["image_link"] + with self.subTest(subtest="test initial value of image_link field"): + self.assertEqual(observed_initial_image, image_link) class ChantDeleteViewTest(TestCase):