Repository: DDMAL/CantusDB

Issue:
broken edit forms for empty sources
If one tries to access either the volpiano or the proofreading form for an empty source e.g. http://206.12.88.113/proofread-chant/710076, one is met with a grey screen of sadness. On OldCantus  https://cantus.uwaterloo.ca/edit-volpiano/710076 gives the explanation page about editing instead.


Discussion:
We should avoid showing users django debug screens, yes! This is not urgent, since http://206.12.88.113/chant-create/710076 still works.

Resolving patch:
diff --git a/django/cantusdb_project/main_app/views/chant.py b/django/cantusdb_project/main_app/views/chant.py
--- a/django/cantusdb_project/main_app/views/chant.py
+++ b/django/cantusdb_project/main_app/views/chant.py
@@ -1410,6 +1410,9 @@ def get_queryset(self):
 
         # get all chants in the specified source
         chants = source.chant_set
+        if not source.chant_set.exists():
+            # return empty queryset
+            return chants.all()
         # filter the chants with optional search params
         if feast_id:
             chants = chants.filter(feast__id=feast_id)
@@ -1437,8 +1440,8 @@ def get_object(self):
             the Chant that we wish to edit (specified by the Chant's pk)
         """
         queryset = self.get_queryset()
-        if len(queryset) == 0:
-            raise Http404("There are no chants associated with this source to edit")
+        if queryset.count() == 0:
+            return None
         pk = self.request.GET.get("pk")
         # if a pk is not specified, this means that the user has not yet selected a Chant to edit
         # thus, we will not render the update form
@@ -1522,7 +1525,7 @@ def get_chants_with_folios(chants_in_feast):
 
         # the following code block is sort of obsolete because if there is no Chant
         # in the Source, a 404 will be raised
-        if chants_in_source.count() == 0:
+        if not chants_in_source.exists():
             # these are needed in the selectors and hyperlinks on the right side of the page
             # if there's no chant in the source, there should be no options in those selectors
             context["folios"] = None
@@ -1541,20 +1544,26 @@ def get_chants_with_folios(chants_in_feast):
         # the options for the feast selector on the right, same as the source detail page
         context["feasts_with_folios"] = get_feast_selector_options(source, folios)
 
-        # the user has selected a folio, or,
-        # they have just navigated to the edit-chant page (where the first folio gets
-        # selected by default)
-        if self.request.GET.get("folio") or (
-            not self.request.GET.get("folio") and not self.request.GET.get("feast")
-        ):
-            # if browsing chants on a specific folio
+        if self.request.GET.get("feast"):
+            # if there is a "feast" query parameter, it means the user has chosen a specific feast
+            # need to render a list of chants, grouped and ordered by folio and within each group,
+            # ordered by c_sequence
+            context["folios_current_feast"] = get_chants_with_folios(self.queryset)
+        else:
+            # the user has selected a folio, or,
+            # they have just navigated to the edit-chant page (where the first folio gets
+            # selected by default)
             if self.request.GET.get("folio"):
+                # if browsing chants on a specific folio
                 folio = self.request.GET.get("folio")
             else:
                 folio = folios[0]
                 # will be used in the template to pre-select the first folio in the drop-down
                 context["initial_GET_folio"] = folio
-            index = list(folios).index(folio)
+            try:
+                index = list(folios).index(folio)
+            except ValueError:
+                raise Http404("No chants within source match the specified folio")
             # get the previous and next folio, if available
             context["previous_folio"] = folios[index - 1] if index != 0 else None
             context["next_folio"] = (
@@ -1564,12 +1573,6 @@ def get_chants_with_folios(chants_in_feast):
             # need to render a list of chants, ordered by c_sequence and grouped by feast
             context["feasts_current_folio"] = get_chants_with_feasts(self.queryset)
 
-        elif self.request.GET.get("feast"):
-            # if there is a "feast" query parameter, it means the user has chosen a specific feast
-            # need to render a list of chants, grouped and ordered by folio and within each group,
-            # ordered by c_sequence
-            context["folios_current_feast"] = get_chants_with_folios(self.queryset)
-
         # this boolean lets us decide whether to show the user the instructions or the editing form
         # if the pk hasn't been specified, a user hasn't selected a specific chant they want to edit
         # if so, we should display the instructions


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
@@ -2020,12 +2020,12 @@ def test_url_and_templates(self):
         self.assertEqual(response.status_code, 404)
         self.assertTemplateUsed(response, "404.html")
 
-        # trying to access chant-edit with a source that has no chant should return 404
+        # trying to access chant-edit with a source that has no chant should return 200
         source2 = make_fake_source()
 
         response = self.client.get(reverse("source-edit-chants", args=[source2.id]))
-        self.assertEqual(response.status_code, 404)
-        self.assertTemplateUsed(response, "404.html")
+        self.assertEqual(response.status_code, 200)
+        self.assertTemplateUsed(response, "chant_edit.html")
 
     def test_update_chant(self):
         source = make_fake_source()