From c2c7f86e33b2eecad9cde1c2b743815a9dcfcaa1 Mon Sep 17 00:00:00 2001
From: Marco Ciotola <848222@stud.unive.it>
Date: Mon, 4 Mar 2019 21:44:35 +0100
Subject: [PATCH] [DJ1.10] Upgrade Celery to 4.2.1, Kombu to 4.2.0

Should probably integrate with django-celery-results and/or django-celery-beat
---
 Procfile             |  2 +-
 helios/__init__.py   |  6 +++-
 helios/celery_app.py | 21 ++++++++++++
 helios/tasks.py      | 79 ++++++++++++++++++++++++--------------------
 helios/tests.py      | 49 ++++++++++++---------------
 requirements.txt     |  5 ++-
 settings.py          | 18 +++-------
 7 files changed, 98 insertions(+), 82 deletions(-)
 create mode 100644 helios/celery_app.py

diff --git a/Procfile b/Procfile
index 63d8d0c..608417d 100644
--- a/Procfile
+++ b/Procfile
@@ -1,2 +1,2 @@
 web: gunicorn wsgi:application -b 0.0.0.0:$PORT -w 8
-worker: python manage.py celeryd -E -B --beat --concurrency=1
\ No newline at end of file
+worker: celery worker --app helios --events --beat --concurrency 1 --logfile celeryw.log --pidfile celeryw.pid
\ No newline at end of file
diff --git a/helios/__init__.py b/helios/__init__.py
index 3a516d7..2f59829 100644
--- a/helios/__init__.py
+++ b/helios/__init__.py
@@ -1,5 +1,9 @@
 from django.conf import settings
-from django.core.urlresolvers import reverse
+# This will make sure the app is always imported when
+# Django starts so that shared_task will use this app.
+from celery_app import app as celery_app
+
+__all__ = ('celery_app', 'TEMPLATE_BASE', 'ADMIN_ONLY', 'VOTERS_UPLOAD', 'VOTERS_EMAIL',)
 
 TEMPLATE_BASE = settings.HELIOS_TEMPLATE_BASE or "helios/templates/base.html"
 
diff --git a/helios/celery_app.py b/helios/celery_app.py
new file mode 100644
index 0000000..89f0ecb
--- /dev/null
+++ b/helios/celery_app.py
@@ -0,0 +1,21 @@
+import os
+
+# set the default Django settings module for the 'celery' program.
+from celery import Celery
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
+
+app = Celery()
+
+# Using a string here means the worker doesn't have to serialize
+# the configuration object to child processes.
+# - namespace='CELERY' means all celery-related configuration keys
+#   should have a `CELERY_` prefix.
+app.config_from_object('django.conf:settings', namespace='CELERY')
+
+# Load task modules from all registered Django app configs.
+app.autodiscover_tasks()
+
+@app.task(bind=True)
+def debug_task(self):
+    print('Request: {0!r}'.format(self.request))
diff --git a/helios/tasks.py b/helios/tasks.py
index d550afc..8610097 100644
--- a/helios/tasks.py
+++ b/helios/tasks.py
@@ -5,8 +5,7 @@ Celery queued tasks for Helios
 ben@adida.net
 """
 import copy
-
-from celery.task import task
+from celery import shared_task
 from celery.utils.log import get_logger
 
 import signals
@@ -14,9 +13,9 @@ from models import CastVote, Election, Voter, VoterFile
 from view_utils import render_template_raw
 
 
-@task()
+@shared_task
 def cast_vote_verify_and_store(cast_vote_id, status_update_message=None, **kwargs):
-    cast_vote = CastVote.objects.get(id = cast_vote_id)
+    cast_vote = CastVote.objects.get(id=cast_vote_id)
     result = cast_vote.verify_and_store()
 
     voter = cast_vote.voter
@@ -26,21 +25,22 @@ def cast_vote_verify_and_store(cast_vote_id, status_update_message=None, **kwarg
     if result:
         # send the signal
         signals.vote_cast.send(sender=election, election=election, user=user, voter=voter, cast_vote=cast_vote)
-        
+
         if status_update_message and user.can_update_status():
             user.update_status(status_update_message)
     else:
         logger = get_logger(cast_vote_verify_and_store.__name__)
         logger.error("Failed to verify and store %d" % cast_vote_id)
-    
-@task()
+
+
+@shared_task
 def voters_email(election_id, subject_template, body_template, extra_vars={},
                  voter_constraints_include=None, voter_constraints_exclude=None):
     """
     voter_constraints_include are conditions on including voters
     voter_constraints_exclude are conditions on excluding voters
     """
-    election = Election.objects.get(id = election_id)
+    election = Election.objects.get(id=election_id)
 
     # select the right list of voters
     voters = election.voter_set.all()
@@ -50,61 +50,66 @@ def voters_email(election_id, subject_template, body_template, extra_vars={},
         voters = voters.exclude(**voter_constraints_exclude)
 
     for voter in voters:
-        single_voter_email.delay(voter.uuid, subject_template, body_template, extra_vars)            
+        single_voter_email.delay(voter.uuid, subject_template, body_template, extra_vars)
+
 
-@task()
+@shared_task
 def voters_notify(election_id, notification_template, extra_vars={}):
-    election = Election.objects.get(id = election_id)
+    election = Election.objects.get(id=election_id)
     for voter in election.voter_set.all():
         single_voter_notify.delay(voter.uuid, notification_template, extra_vars)
 
-@task()
+
+@shared_task
 def single_voter_email(voter_uuid, subject_template, body_template, extra_vars={}):
-    voter = Voter.objects.get(uuid = voter_uuid)
+    voter = Voter.objects.get(uuid=voter_uuid)
 
     the_vars = copy.copy(extra_vars)
-    the_vars.update({'voter' : voter})
+    the_vars.update({'voter': voter})
 
     subject = render_template_raw(None, subject_template, the_vars)
     body = render_template_raw(None, body_template, the_vars)
 
     voter.send_message(subject, body)
 
-@task()
+
+@shared_task
 def single_voter_notify(voter_uuid, notification_template, extra_vars={}):
-    voter = Voter.objects.get(uuid = voter_uuid)
+    voter = Voter.objects.get(uuid=voter_uuid)
 
     the_vars = copy.copy(extra_vars)
-    the_vars.update({'voter' : voter})
+    the_vars.update({'voter': voter})
 
     notification = render_template_raw(None, notification_template, the_vars)
 
     voter.send_notification(notification)
 
-@task()
+
+@shared_task
 def election_compute_tally(election_id):
-    election = Election.objects.get(id = election_id)
+    election = Election.objects.get(id=election_id)
     election.compute_tally()
 
-    election_notify_admin.delay(election_id = election_id,
-                                subject = "encrypted tally computed",
-                                body = """
+    election_notify_admin.delay(election_id=election_id,
+                                subject="encrypted tally computed",
+                                body="""
 The encrypted tally for election %s has been computed.
 
 --
 Helios
 """ % election.name)
-                                
+
     if election.has_helios_trustee():
-        tally_helios_decrypt.delay(election_id = election.id)
+        tally_helios_decrypt.delay(election_id=election.id)
 
-@task()
+
+@shared_task
 def tally_helios_decrypt(election_id):
-    election = Election.objects.get(id = election_id)
+    election = Election.objects.get(id=election_id)
     election.helios_trustee_decrypt()
-    election_notify_admin.delay(election_id = election_id,
-                                subject = 'Helios Decrypt',
-                                body = """
+    election_notify_admin.delay(election_id=election_id,
+                                subject='Helios Decrypt',
+                                body="""
 Helios has decrypted its portion of the tally
 for election %s.
 
@@ -112,13 +117,14 @@ for election %s.
 Helios
 """ % election.name)
 
-@task()
+
+@shared_task
 def voter_file_process(voter_file_id):
-    voter_file = VoterFile.objects.get(id = voter_file_id)
+    voter_file = VoterFile.objects.get(id=voter_file_id)
     voter_file.process()
-    election_notify_admin.delay(election_id = voter_file.election.id, 
-                                subject = 'voter file processed',
-                                body = """
+    election_notify_admin.delay(election_id=voter_file.election.id,
+                                subject='voter file processed',
+                                body="""
 Your voter file upload for election %s
 has been processed.
 
@@ -128,7 +134,8 @@ has been processed.
 Helios
 """ % (voter_file.election.name, voter_file.num_voters))
 
-@task()
+
+@shared_task
 def election_notify_admin(election_id, subject, body):
-    election = Election.objects.get(id = election_id)
+    election = Election.objects.get(id=election_id)
     election.admin.send_message(subject, body)
diff --git a/helios/tests.py b/helios/tests.py
index c87f74a..1e97188 100644
--- a/helios/tests.py
+++ b/helios/tests.py
@@ -2,29 +2,24 @@
 Unit Tests for Helios
 """
 
-import unittest, datetime, re, urllib
-import django_webtest
-
-import models
-import datatypes
-
-from helios_auth import models as auth_models
-from views import ELGAMAL_PARAMS
-import views
-import utils
+import datetime
+import re
+import urllib
 
-from django.db import IntegrityError, transaction
-from django.test.client import Client
+import django_webtest
+import uuid
+from django.conf import settings
+from django.core import mail
+from django.core.files import File
 from django.test import TestCase
 from django.utils.html import escape as html_escape
 
-from django.core import mail
-from django.core.files import File
-from django.core.urlresolvers import reverse
-from django.conf import settings
-from django.core.exceptions import PermissionDenied
+import helios.datatypes as datatypes
+import helios.models as models
+import helios.utils as utils
+import helios.views as views
+from helios_auth import models as auth_models
 
-import uuid
 
 class ElectionModelTests(TestCase):
     fixtures = ['users.json']
@@ -42,7 +37,7 @@ class ElectionModelTests(TestCase):
         self.election.questions = QUESTIONS
 
     def setup_trustee(self):
-        self.election.generate_trustee(ELGAMAL_PARAMS)
+        self.election.generate_trustee(views.ELGAMAL_PARAMS)
 
     def setup_openreg(self):
         self.election.openreg=True
@@ -115,7 +110,7 @@ class ElectionModelTests(TestCase):
         self.assertEquals(len(issues), 0)
         
     def test_helios_trustee(self):
-        self.election.generate_trustee(ELGAMAL_PARAMS)
+        self.election.generate_trustee(views.ELGAMAL_PARAMS)
 
         self.assertTrue(self.election.has_helios_trustee())
 
@@ -199,15 +194,15 @@ class ElectionModelTests(TestCase):
     def test_voter_registration(self):
         # before adding a voter
         voters = models.Voter.get_by_election(self.election)
-        self.assertTrue(len(voters) == 0)
+        self.assertEquals(0, len(voters))
 
         # make sure no voter yet
         voter = models.Voter.get_by_election_and_user(self.election, self.user)
-        self.assertTrue(voter == None)
+        self.assertIsNone(voter)
 
         # make sure no voter at all across all elections
         voters = models.Voter.get_by_user(self.user)
-        self.assertTrue(len(voters) == 0)
+        self.assertEquals(0, len(voters))
 
         # register the voter
         voter = models.Voter.register_user_in_election(self.user, self.election)
@@ -215,13 +210,13 @@ class ElectionModelTests(TestCase):
         # make sure voter is there now
         voter_2 = models.Voter.get_by_election_and_user(self.election, self.user)
 
-        self.assertFalse(voter == None)
-        self.assertFalse(voter_2 == None)
+        self.assertIsNotNone(voter)
+        self.assertIsNotNone(voter_2)
         self.assertEquals(voter, voter_2)
 
         # make sure voter is there in this call too
         voters = models.Voter.get_by_user(self.user)
-        self.assertTrue(len(voters) == 1)
+        self.assertEquals(1, len(voters))
         self.assertEquals(voter, voters[0])
 
         voter_2 = models.Voter.get_by_election_and_uuid(self.election, voter.uuid)
@@ -275,7 +270,7 @@ class DatatypeTests(TestCase):
 
     def setUp(self):
         self.election = models.Election.objects.all()[0]
-        self.election.generate_trustee(ELGAMAL_PARAMS)
+        self.election.generate_trustee(views.ELGAMAL_PARAMS)
 
     def test_instantiate(self):
         ld_obj = datatypes.LDObject.instantiate(self.election.get_helios_trustee(), '2011/01/Trustee')
diff --git a/requirements.txt b/requirements.txt
index 1211f93..99e4906 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,9 +1,8 @@
 Django==1.9.13
 anyjson==0.3.3
-celery==3.1.25
-django-celery==3.2.2
+celery==4.2.1
 django-picklefield==0.3.0
-kombu==3.0.37
+kombu==4.2.0
 html5lib==0.999
 psycopg2==2.7.3.2
 pyparsing==1.5.7
diff --git a/settings.py b/settings.py
index 34c87d1..9b30ff5 100644
--- a/settings.py
+++ b/settings.py
@@ -157,9 +157,6 @@ INSTALLED_APPS = (
     'djangosecure',
     'django.contrib.sessions',
     'django.contrib.sites',
-    ## needed for queues
-    'djcelery',
-    'kombu.transport.django',
     ## HELIOS stuff
     'helios_auth',
     'helios',
@@ -274,17 +271,10 @@ logging.basicConfig(
 )
 
 
-# set up django-celery
-# BROKER_BACKEND = "kombu.transport.DatabaseTransport"
-BROKER_URL = "django://"
-CELERY_RESULT_DBURI = DATABASES['default']
-import djcelery
-djcelery.setup_loader()
-
-
-# for testing
-TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'
-# this effectively does CELERY_ALWAYS_EAGER = True
+# set up celery
+CELERY_BROKER_URL = 'amqp://localhost'
+CELERY_TASK_ALWAYS_EAGER = True
+#database_url = DATABASES['default']
 
 # Rollbar Error Logging
 ROLLBAR_ACCESS_TOKEN = get_from_env('ROLLBAR_ACCESS_TOKEN', None)
-- 
GitLab