Repository: DDMAL/CantusDB

Issue:
Next Feast suggester on Create Chant page
Beneath the Feast datalist on the Create Chant page, there is a list of feasts that follow the feast to which chants are currently being added. Similar to the Next Chant suggester in the sidebar, I assume this searches through all the manuscripts and finds all instances of feast currently being edited, finds the feast that follows the current feast in that manuscript, and lists all such following feasts, keeping track of how often the two feasts are found next to each other.

![Screen Shot 2022-06-01 at 15 42 34](https://user-images.githubusercontent.com/58090591/171488569-0a1be1d4-457f-4ca0-9108-5565e82e41fc.png)

further details:

- Clicking on one of the generated Feast links updates the Feast datalist.
- There doesn't seem to be any limit to how many feasts are displayed.


Discussion:

Resolving patch:
diff --git a/django/cantusdb_project/main_app/forms.py b/django/cantusdb_project/main_app/forms.py
--- a/django/cantusdb_project/main_app/forms.py
+++ b/django/cantusdb_project/main_app/forms.py
@@ -42,6 +42,7 @@ class Meta:
             "feast",
             "mode",
             "differentia",
+            "differentia_id",
             "finalis",
             "extra",
             "chant_range",
@@ -66,6 +67,7 @@ class Meta:
             #'feast': SelectWidget(),
             "mode": TextInputWidget(),
             "differentia": TextInputWidget(),
+            "differentia_id": TextInputWidget(),
             "finalis": TextInputWidget(),
             "extra": TextInputWidget(),
             "chant_range": VolpianoInputWidget(),
diff --git a/django/cantusdb_project/main_app/models/chant.py b/django/cantusdb_project/main_app/models/chant.py
--- a/django/cantusdb_project/main_app/models/chant.py
+++ b/django/cantusdb_project/main_app/models/chant.py
@@ -57,6 +57,7 @@ class Chant(BaseModel):
     feast = models.ForeignKey("Feast", on_delete=models.PROTECT, null=True, blank=True)
     mode = models.CharField(max_length=63, null=True, blank=True)
     differentia = models.CharField(blank=True, null=True, max_length=63)
+    differentia_id = models.CharField(blank=True, null=True, max_length=12)
     finalis = models.CharField(blank=True, null=True, max_length=63)
     extra = models.CharField(blank=True, null=True, max_length=63)
     chant_range = models.CharField(
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
@@ -21,6 +21,7 @@
 from django.contrib.auth.mixins import LoginRequiredMixin
 from django.http import Http404
 from next_chants import next_chants
+from collections import Counter
 
 
 def keyword_search(queryset: QuerySet, keywords: str) -> QuerySet:
@@ -652,6 +653,36 @@ def make_suggested_chant_dict(suggested_chant):
 
         return suggested_chants_dicts
 
+    def get_suggested_feasts(self):
+        """based on the feast of the most recently edited chant, provide a list of suggested feasts that
+        might follow the feast of that chant.
+
+        Returns: a dictionary, with feast objects as keys and counts as values
+        """
+        try:
+            latest_chant = self.source.chant_set.latest("date_updated")
+        except Chant.DoesNotExist:
+            return None
+
+        current_feast = latest_chant.feast
+        chants_assocd_w_current_feast = Chant.objects.filter(feast=current_feast)
+        next_chants = [chant.get_next_chant()
+            for chant
+            in chants_assocd_w_current_feast
+            ]
+        next_feasts = [chant.feast
+            for chant
+            in next_chants
+            if type(chant) is Chant # .get_next_chant() sometimes returns None
+                and chant.feast is not None # some chants aren't associated with a feast
+                and chant.feast.id != current_feast.id
+            ]
+        feast_counts = Counter(next_feasts)
+        sorted_feast_counts = dict( sorted(feast_counts.items(),
+                           key=lambda item: item[1],
+                           reverse=True))
+        return sorted_feast_counts
+
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
         context["source"] = self.source
@@ -660,6 +691,7 @@ def get_context_data(self, **kwargs):
         except Chant.DoesNotExist:
             context["previous_chant"] = None
         context["suggested_chants"] = self.get_suggested_chants()
+        context["suggested_feasts"] = self.get_suggested_feasts()
         return context
 
     def form_valid(self, form):


Test patch: