Repository: carpentries/amy

Issue:
Support for multiple hosts
Some events have multiple hosts. Currently, in the introduction email, only the first host is being used.

Could we make `host` a list like `instructors` and `supporting_instructors`?

https://github.com/carpentries/amy/blob/develop/amy/autoemails/actions.py#L758

```diff
--- context["host"] = hosts[0].person
+++ context["host"] = [instr for instr in hosts]
```





Discussion:
Can we make it so that only one person is actually named in the email text, but all the others are included when the email is sent? 
Just to be clear (as I was confused when I first read this): This refers to the _Host role_, not the _Host organization._  
Yes! The Host Role :) 
@pbanaszkiewicz  Please let us know if you have any questions about this issue.
@maneesha @sheraaronhurt @fmichonneau it's all clear for me.

Resolving patch:
diff --git a/amy/autoemails/actions.py b/amy/autoemails/actions.py
--- a/amy/autoemails/actions.py
+++ b/amy/autoemails/actions.py
@@ -231,7 +231,7 @@ def all_recipients(self) -> str:
             return ""
 
     @staticmethod
-    def check(task: Task):
+    def check(task: Task):  # type: ignore
         """Conditions for creating a NewInstructorAction."""
         return bool(
             # 2019-11-01: we accept instructors without `may_contact` agreement
@@ -327,7 +327,7 @@ def all_recipients(self) -> str:
             return ""
 
     @staticmethod
-    def check(task: Task):
+    def check(task: Task):  # type: ignore
         """Conditions for creating a NewSupportingInstructorAction."""
         return bool(
             task.role.name == "supporting-instructor"
@@ -457,7 +457,7 @@ def all_recipients(self) -> str:
             return ""
 
     @staticmethod
-    def check(event: Event):
+    def check(event: Event):  # type: ignore
         """Conditions for creating a PostWorkshopAction."""
         return bool(
             # end date is required and in future
@@ -515,6 +515,11 @@ def get_additional_context(self, objects, *args, **kwargs):
         context["helpers"] = list(
             Person.objects.filter(task__in=event.task_set.filter(role__name="helper"))
         )
+        context["hosts"] = list(
+            Person.objects.filter(
+                task__in=event.task_set.filter(role__name="host")
+            )
+        )
 
         # querying over Person.objects lets us get rid of duplicates
         person_emails = list(
@@ -585,7 +590,7 @@ def all_recipients(self) -> str:
             return ""
 
     @staticmethod
-    def check(event: Event):
+    def check(event: Event):  # type: ignore
         """Conditions for creating a SelfOrganisedRequestAction."""
         try:
             return bool(
@@ -697,7 +702,7 @@ def recipients(self):
             return None
 
     @staticmethod
-    def check(event: Event):
+    def check(event: Event):  # type: ignore
         """Conditions for creating a SelfOrganisedRequestAction."""
         # there is 1 host task and 2 instructor tasks
         try:
@@ -756,6 +761,7 @@ def get_additional_context(self, objects, *args, **kwargs):
         context["instructors"] = [instr.person for instr in instructors]
         context["supporting_instructors"] = [instr.person for instr in support]
         context["host"] = hosts[0].person
+        context["hosts"] = [host.person for host in hosts]
         context["instructor1"] = instructors[0].person
         context["instructor2"] = instructors[1].person
 
@@ -858,7 +864,7 @@ def all_recipients(self) -> str:
             return ""
 
     @staticmethod
-    def check(event: Event):
+    def check(event: Event):  # type: ignore
         """Conditions for creating a AskForWebsiteAction."""
         instructors = event.task_set.filter(
             role__name__in=AskForWebsiteAction.role_names
@@ -902,6 +908,8 @@ def get_additional_context(self, objects, *args, **kwargs):
         # people
         instructors = event.task_set.filter(role__name__in=self.role_names)
         context["instructors"] = [instr.person for instr in instructors]
+        hosts = event.task_set.filter(role__name="host")
+        context["hosts"] = [host.person for host in hosts]
 
         task_emails = [task.person.email for task in instructors]
         context["all_emails"] = list(filter(bool, task_emails))
@@ -988,7 +996,7 @@ def all_recipients(self) -> str:
             return ""
 
     @staticmethod
-    def check(event: Event):
+    def check(event: Event):  # type: ignore
         """Conditions for creating a RecruitHelpersAction."""
         hosts = event.task_set.filter(role__name="host")
         instructors = event.task_set.filter(role__name="instructor")


Test patch:
diff --git a/amy/autoemails/tests/test_askforwebsiteaction.py b/amy/autoemails/tests/test_askforwebsiteaction.py
--- a/amy/autoemails/tests/test_askforwebsiteaction.py
+++ b/amy/autoemails/tests/test_askforwebsiteaction.py
@@ -203,10 +203,18 @@ def testContext(self):
             username="hgranger",
             email="hg@magic.uk",
         )
+        p3 = Person.objects.create(
+            personal="Ron",
+            family="Weasley",
+            username="rweasley",
+            email="rw@magic.uk",
+        )
         instructor = Role.objects.create(name="instructor")
         supporting = Role.objects.create(name="supporting-instructor")
+        host = Role.objects.create(name="host")
         Task.objects.create(event=e, person=p1, role=instructor)
         Task.objects.create(event=e, person=p2, role=supporting)
+        Task.objects.create(event=e, person=p3, role=host)
 
         ctx = a.get_additional_context(objects=dict(event=e))
         self.assertEqual(
@@ -218,6 +226,7 @@ def testContext(self):
                 workshop_host=Organization.objects.first(),
                 regional_coordinator_email=["admin-uk@carpentries.org"],
                 instructors=[p1, p2],
+                hosts=[p3],
                 all_emails=["hp@magic.uk", "hg@magic.uk"],
                 assignee="Regional Coordinator",
                 tags=["SWC", "TTT"],
diff --git a/amy/autoemails/tests/test_instructorshostintroductionaction.py b/amy/autoemails/tests/test_instructorshostintroductionaction.py
--- a/amy/autoemails/tests/test_instructorshostintroductionaction.py
+++ b/amy/autoemails/tests/test_instructorshostintroductionaction.py
@@ -216,6 +216,7 @@ def testContext(self):
             ],
             supporting_instructor1=supporting_instructor1.person,
             supporting_instructor2=supporting_instructor2.person,
+            hosts=[host.person],
             all_emails=[
                 "hp@magic.uk",
                 "rw@magic.uk",
diff --git a/amy/autoemails/tests/test_postworkshopaction.py b/amy/autoemails/tests/test_postworkshopaction.py
--- a/amy/autoemails/tests/test_postworkshopaction.py
+++ b/amy/autoemails/tests/test_postworkshopaction.py
@@ -234,6 +234,7 @@ def testContext(self):
                 instructors=[p2],
                 supporting_instructors=[p4],
                 helpers=[p1, p3],
+                hosts=[p1],
                 all_emails=[
                     "hg@magic.uk",
                     "draco@malfoy.com",
@@ -375,4 +376,7 @@ def test_all_recipients(self):
             objects=dict(event=e),
         )
 
-        self.assertEqual(a.all_recipients(), "hg@magic.uk, draco@malfoy.com, hp@magic.uk, rw@magic.uk")
+        self.assertEqual(
+            a.all_recipients(),
+            "hg@magic.uk, draco@malfoy.com, hp@magic.uk, rw@magic.uk"
+        )