Repository: carpentries/amy

Issue:
Error cancelling a scheduled Introduction email
The error message says:

```python
Invalid input of type: 'Job'. Convert to a bytes, string, int or float first.
```

And it's reproducible.


Discussion:

Resolving patch:
diff --git a/amy/autoemails/admin.py b/amy/autoemails/admin.py
--- a/amy/autoemails/admin.py
+++ b/amy/autoemails/admin.py
@@ -12,10 +12,9 @@
 import django_rq
 from markdownx.widgets import AdminMarkdownxWidget
 from rq.exceptions import NoSuchJobError
-from rq.job import Job
-from rq_scheduler.utils import from_unix
 
 from autoemails.forms import RescheduleForm, TemplateForm
+from autoemails.job import Job
 from autoemails.models import EmailTemplate, Trigger, RQJob
 from autoemails.utils import (
     check_status,
@@ -368,6 +367,8 @@ def cancel(self, request, object_id):
         if job.is_queued or not job.get_status():
             job.cancel()  # for "pure" jobs
             scheduler.cancel(job)  # for scheduler-based jobs
+            rqjob.status = "cancelled"
+            rqjob.save()
 
             logger.debug(f"Job {rqjob.job_id} was cancelled.")
             messages.info(request, f"The job {rqjob.job_id} was cancelled.")
diff --git a/amy/autoemails/base_views.py b/amy/autoemails/base_views.py
--- a/amy/autoemails/base_views.py
+++ b/amy/autoemails/base_views.py
@@ -2,8 +2,8 @@
 from django.urls import reverse
 from django.utils.html import format_html
 from rq.exceptions import NoSuchJobError
-from rq.job import Job
 
+from autoemails.job import Job
 from autoemails.utils import (
     check_status,
     scheduled_execution_time,
diff --git a/amy/workshops/views.py b/amy/workshops/views.py
--- a/amy/workshops/views.py
+++ b/amy/workshops/views.py
@@ -1530,6 +1530,7 @@ def form_valid(self, form):
 
         seat_membership = form.cleaned_data["seat_membership"]
         event = form.cleaned_data["event"]
+        check_ihia_old = InstructorsHostIntroductionAction.check(event)
 
         # check associated membership remaining seats and validity
         if hasattr(self, "request") and seat_membership is not None:
@@ -1604,7 +1605,10 @@ def form_valid(self, form):
             )
 
         # check conditions for running a InstructorsHostIntroductionAction
-        if InstructorsHostIntroductionAction.check(self.object.event):
+        if (
+            not check_ihia_old
+            and InstructorsHostIntroductionAction.check(self.object.event)
+        ):
             triggers = Trigger.objects.filter(
                 active=True, action="instructors-host-introduction"
             )
@@ -1614,7 +1618,7 @@ def form_valid(self, form):
                 scheduler=scheduler,
                 triggers=triggers,
                 context_objects=dict(event=self.object.event),
-                object_=self.object,
+                object_=self.object.event,
                 request=self.request,
             )
 
@@ -1712,13 +1716,13 @@ def form_valid(self, form):
                 scheduler=scheduler,
                 triggers=triggers,
                 context_objects=dict(event=self.object.event),
-                object_=self.object,
+                object_=self.object.event,
                 request=self.request,
             )
 
         # InstructorsHostIntroductionAction conditions were met, but aren't anymore
         elif check_ihi_old and not check_ihi_new:
-            jobs = self.object.rq_jobs.filter(
+            jobs = self.object.event.rq_jobs.filter(
                 trigger__action="instructors-host-introduction"
             )
             ActionManageMixin.remove(
@@ -1727,7 +1731,7 @@ def form_valid(self, form):
                 scheduler=scheduler,
                 connection=redis_connection,
                 jobs=jobs.values_list("job_id", flat=True),
-                object_=self.object,
+                object_=self.object.event,
                 request=self.request,
             )
 
@@ -1765,7 +1769,7 @@ def before_delete(self, *args, **kwargs):
             request=self.request,
         )
 
-        jobs = self.object.rq_jobs.filter(
+        jobs = self.object.event.rq_jobs.filter(
             trigger__action="instructors-host-introduction"
         )
         ActionManageMixin.remove(
@@ -1774,7 +1778,7 @@ def before_delete(self, *args, **kwargs):
             scheduler=scheduler,
             connection=redis_connection,
             jobs=jobs.values_list("job_id", flat=True),
-            object_=self.object,
+            object_=self.object.event,
             request=self.request,
         )
 


Test patch:
diff --git a/amy/autoemails/tests/base.py b/amy/autoemails/tests/base.py
--- a/amy/autoemails/tests/base.py
+++ b/amy/autoemails/tests/base.py
@@ -1,8 +1,9 @@
 import django_rq
 from fakeredis import FakeStrictRedis
-from redis import Redis
 from rq import Queue
 
+from autoemails.job import Job
+
 
 connection = FakeStrictRedis()
 
@@ -24,7 +25,7 @@ def setUp(self):
         super().setUp()
         self.connection = connection
         # self.connection = Redis()
-        self.queue = Queue(is_async=False, connection=self.connection)
+        self.queue = Queue(is_async=False, connection=self.connection, job_class=Job)
         self.scheduler = django_rq.get_scheduler('testing', queue=self.queue)
         self.scheduler.connection = self.connection
 
diff --git a/amy/autoemails/tests/test_actionmanagemixin.py b/amy/autoemails/tests/test_actionmanagemixin.py
--- a/amy/autoemails/tests/test_actionmanagemixin.py
+++ b/amy/autoemails/tests/test_actionmanagemixin.py
@@ -3,11 +3,11 @@
 
 from django.test import TestCase, RequestFactory
 from rq.exceptions import NoSuchJobError
-from rq.job import Job
 from rq_scheduler.utils import to_unix
 
 from autoemails.actions import NewInstructorAction
 from autoemails.base_views import ActionManageMixin
+from autoemails.job import Job
 from autoemails.models import EmailTemplate, Trigger, RQJob
 from autoemails.tests.base import FakeRedisTestCaseMixin, dummy_job
 from workshops.models import (
diff --git a/amy/autoemails/tests/test_admin_cancel.py b/amy/autoemails/tests/test_admin_cancel.py
--- a/amy/autoemails/tests/test_admin_cancel.py
+++ b/amy/autoemails/tests/test_admin_cancel.py
@@ -1,14 +1,13 @@
-from datetime import datetime, timedelta
+from datetime import timedelta
 
 from django.test import TestCase
 from django.urls import reverse
 from rq import Queue
 from rq.exceptions import NoSuchJobError
-from rq.job import Job
-from rq_scheduler.utils import to_unix
 
 from autoemails import admin
 from autoemails.models import EmailTemplate, Trigger, RQJob
+from autoemails.job import Job
 from autoemails.tests.base import FakeRedisTestCaseMixin, dummy_job
 from workshops.tests.base import SuperuserMixin
 
@@ -149,6 +148,10 @@ def test_enqueued_job_cancelled(self):
                 )
             )
 
+        # job status updated
+        rqjob.refresh_from_db()
+        self.assertEqual(rqjob.status, "cancelled")
+
         # job data still available
         Job.fetch(rqjob.job_id, connection=self.scheduler.connection)
         # ...but nothing is scheduled
diff --git a/amy/autoemails/tests/test_admin_edit_template.py b/amy/autoemails/tests/test_admin_edit_template.py
--- a/amy/autoemails/tests/test_admin_edit_template.py
+++ b/amy/autoemails/tests/test_admin_edit_template.py
@@ -1,14 +1,13 @@
-from datetime import date, datetime, timedelta, timezone
+from datetime import date, timedelta
 
 from django.test import TestCase
 from django.urls import reverse
 from rq.exceptions import NoSuchJobError
-from rq.job import Job
-from rq_scheduler.utils import to_unix
 
 from autoemails import admin
 from autoemails.actions import NewInstructorAction
 from autoemails.models import EmailTemplate, Trigger, RQJob
+from autoemails.job import Job
 from autoemails.tests.base import FakeRedisTestCaseMixin, dummy_job
 from workshops.models import Event, Organization, Person, Role, Task
 from workshops.tests.base import SuperuserMixin
@@ -36,14 +35,15 @@ def setUp(self):
         # test event and task
         LC_org = Organization.objects.create(domain="librarycarpentry.org",
                                              fullname="Library Carpentry")
-        self.event =Event.objects.create(
+        self.event = Event.objects.create(
             slug="test-event",
             host=Organization.objects.first(),
             administrator=LC_org,
             start=date.today() + timedelta(days=7),
             end=date.today() + timedelta(days=8),
         )
-        p = Person.objects.create(personal="Harry", family="Potter", email="hp@magic.uk")
+        p = Person.objects.create(personal="Harry", family="Potter",
+                                  email="hp@magic.uk")
         r = Role.objects.create(name="instructor")
         self.task = Task.objects.create(event=self.event, person=p, role=r)
 
diff --git a/amy/autoemails/tests/test_admin_reschedule.py b/amy/autoemails/tests/test_admin_reschedule.py
--- a/amy/autoemails/tests/test_admin_reschedule.py
+++ b/amy/autoemails/tests/test_admin_reschedule.py
@@ -3,11 +3,11 @@
 from django.test import TestCase
 from django.urls import reverse
 from rq.exceptions import NoSuchJobError
-from rq.job import Job
 from rq_scheduler.utils import to_unix
 
 from autoemails import admin
 from autoemails.models import EmailTemplate, Trigger, RQJob
+from autoemails.job import Job
 from autoemails.tests.base import FakeRedisTestCaseMixin, dummy_job
 from workshops.tests.base import SuperuserMixin
 
diff --git a/amy/autoemails/tests/test_admin_retry.py b/amy/autoemails/tests/test_admin_retry.py
--- a/amy/autoemails/tests/test_admin_retry.py
+++ b/amy/autoemails/tests/test_admin_retry.py
@@ -1,14 +1,13 @@
-from datetime import datetime, timedelta
+from datetime import timedelta
 
 from django.test import TestCase
 from django.urls import reverse
 from rq import Queue, SimpleWorker
 from rq.exceptions import NoSuchJobError
-from rq.job import Job
-from rq_scheduler.utils import to_unix
 
 from autoemails import admin
 from autoemails.models import EmailTemplate, Trigger, RQJob
+from autoemails.job import Job
 from autoemails.tests.base import (
     FakeRedisTestCaseMixin,
     dummy_job,
diff --git a/amy/autoemails/tests/test_admin_sendnow.py b/amy/autoemails/tests/test_admin_sendnow.py
--- a/amy/autoemails/tests/test_admin_sendnow.py
+++ b/amy/autoemails/tests/test_admin_sendnow.py
@@ -1,13 +1,13 @@
 from datetime import datetime, timedelta
 
-from django.test import TestCase, Client
+from django.test import TestCase
 from django.urls import reverse
 from rq.exceptions import NoSuchJobError
-from rq.job import Job
 from rq_scheduler.utils import to_unix
 
 from autoemails import admin
 from autoemails.models import EmailTemplate, Trigger, RQJob
+from autoemails.job import Job
 from autoemails.tests.base import FakeRedisTestCaseMixin, dummy_job
 from workshops.tests.base import SuperuserMixin