Repository: DDMAL/CantusDB

Issue:
Contruct source-level csv export with source `siglum` rather than chant `siglum`
Brought to our attention by the fact that the csv export for `D-WI1 2` (https://cantusdatabase.org/source/588308) contains chants with two different sigla, one correct and one incorrect. This seems to be because we are using the chant's `siglum` field in the construction of this csv, which may contain legacy data, rather than the source's `siglum` field, which was corrected. 




Discussion:

Resolving patch:
diff --git a/django/cantusdb_project/main_app/views/views.py b/django/cantusdb_project/main_app/views/views.py
--- a/django/cantusdb_project/main_app/views/views.py
+++ b/django/cantusdb_project/main_app/views/views.py
@@ -235,6 +235,7 @@ def csv_export(request, source_id):
             "node_id",
         ]
     )
+    siglum = source.siglum
     for entry in entries:
         feast = entry.feast.name if entry.feast else ""
         office = entry.office.name if entry.office 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


Test patch:
diff --git a/django/cantusdb_project/main_app/tests/test_views.py b/django/cantusdb_project/main_app/tests/test_views.py
--- 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=","))
@@ -5215,11 +5222,21 @@ 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))
+        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)
@@ -5245,12 +5262,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):