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):