Skip to content

Commit 3442c4c

Browse files
authored
Merge pull request #49 from Xpirix/celery_scheduled_task
Run auto reject organization with celery scheduled jobs.
2 parents a22eb6b + 18e49e6 commit 3442c4c

File tree

10 files changed

+150
-61
lines changed

10 files changed

+150
-61
lines changed

deployment/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ web:
2121
@echo "------------------------------------------------------------------"
2222
@echo "Running in production mode"
2323
@echo "------------------------------------------------------------------"
24-
@docker compose up -d web
24+
@docker compose up -d web worker beat
2525
@# Dont confuse this with the dbbackup make command below
2626
@# This one runs the postgis-backup cron container
2727
@# We add --no-recreate so that it does not destroy & recreate the db container
@@ -366,7 +366,7 @@ devweb: db
366366
@echo "------------------------------------------------------------------"
367367
@echo "Running in DEVELOPMENT mode"
368368
@echo "------------------------------------------------------------------"
369-
@docker compose up --no-deps -d devweb webpack
369+
@docker compose up --no-deps -d devweb webpack rabbitmq worker beat
370370

371371
devweb-runserver: devweb
372372
@echo

deployment/docker-compose.override.example.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,11 @@ services:
6161
- ../django_project:/home/web/django_project
6262
- ${MEDIA_VOLUME}:/home/web/media:rw
6363
- ./logs:/var/log
64+
65+
beat:
66+
volumes:
67+
- ../django_project:/home/web/django_project
68+
69+
worker:
70+
volumes:
71+
- ../django_project:/home/web/django_project

deployment/docker-compose.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ volumes:
1313
media-data:
1414
reports-data:
1515
nginx-conf:
16+
rabbitmq:
17+
celerybeat-schedule:
1618
services:
1719

1820
db:
@@ -73,6 +75,7 @@ services:
7375
- media-data:/home/web/media:rw
7476
- reports-data:/home/web/reports
7577
- ./docker/uwsgi.conf:/uwsgi.conf
78+
- celerybeat-schedule:/home/web/celerybeat-schedule:rw
7679
command: bash -c "npm install && npm run build && uwsgi --ini /uwsgi.conf"
7780
links:
7881
- db:db
@@ -136,6 +139,44 @@ services:
136139
networks:
137140
internal:
138141

142+
rabbitmq:
143+
image: ${RABBITMQ_IMAGE:-rabbitmq:3.7-alpine}
144+
hostname: rabbitmq
145+
volumes:
146+
- rabbitmq:/var/lib/rabbitmq
147+
restart: unless-stopped
148+
networks:
149+
internal:
150+
healthcheck:
151+
test: ["CMD", "rabbitmqctl", "status"]
152+
interval: 10s
153+
timeout: 5s
154+
retries: 5
155+
156+
beat:
157+
<<: *uwsgi-common
158+
depends_on:
159+
rabbitmq:
160+
condition: service_healthy
161+
working_dir: /home/web/django_project
162+
entrypoint: [ ]
163+
command: celery --app=certification.celery:app beat -s /home/web/celerybeat-schedule/schedule -l INFO
164+
networks:
165+
internal:
166+
167+
worker:
168+
<<: *uwsgi-common
169+
depends_on:
170+
db:
171+
condition: service_started
172+
beat:
173+
condition: service_started
174+
working_dir: /home/web/django_project
175+
entrypoint: []
176+
command: celery -A certification worker -l INFO
177+
networks:
178+
internal:
179+
139180
# This is the entry point for a development server.
140181
# Run with --no-deps to run attached to the services
141182
# from prod environment if wanted

deployment/docker/REQUIREMENTS.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,7 @@ django-webpack-loader~=3.1
6060
sorl-thumbnail~=12.11
6161

6262
# Resend email
63-
resend~=2.7
63+
resend~=2.7
64+
65+
# Celery
66+
celery~=5.3
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from __future__ import absolute_import, unicode_literals
2+
3+
# This will make sure the app is always imported when
4+
# Django starts so that shared_task will use this app.
5+
from .celery import app as celery_app
6+
7+
default_app_config = "certification.apps.CertificationConfig"
8+
__all__ = [
9+
"celery_app",
10+
]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from __future__ import absolute_import
2+
3+
import os
4+
5+
from celery import Celery
6+
import logging
7+
8+
logger = logging.getLogger('certification')
9+
10+
11+
# set the default Django settings module for the 'celery' program.
12+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings.base")
13+
14+
app = Celery("certification")
15+
16+
# Using a string here means the worker doesn't have to serialize
17+
# the configuration object to child processes.
18+
# - namespace='CELERY' means all celery-related configuration keys
19+
# should have a `CELERY_` prefix.
20+
app.config_from_object("django.conf:settings", namespace="CELERY")
21+
22+
# Load task modules from all registered Django app configs.
23+
app.autodiscover_tasks()
24+
25+
26+
@app.task(bind=True)
27+
def debug_task(self):
28+
print("Request: {0!r}".format(self.request))

django_project/certification/management/commands/reject_pending_organisations.py

Lines changed: 0 additions & 58 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from certification.tasks.reject_pending_organisations import *
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from celery import shared_task
2+
from django.utils import timezone
3+
from datetime import timedelta
4+
from ..models.certifying_organisation import CertifyingOrganisation
5+
from ..models.status import Status
6+
from ..views import send_rejection_email
7+
8+
9+
@shared_task
10+
def reject_pending_organisations(days=365):
11+
"""Celery task to reject pending certifying organisations
12+
that were created more than one year ago.
13+
"""
14+
print('Begin process....')
15+
print(f'Number of days: {days}')
16+
one_year_ago = timezone.now() - timedelta(days=days)
17+
old_organisations = CertifyingOrganisation.unapproved_objects.all()
18+
19+
print('Begin process to reject old certifying organisations.')
20+
21+
count = 0
22+
for organisation in old_organisations:
23+
if organisation.update_date < one_year_ago:
24+
rejected_status, created = Status.objects.get_or_create(
25+
name='Rejected',
26+
project=organisation.project
27+
)
28+
organisation.status = rejected_status
29+
organisation.rejected = True
30+
organisation.save()
31+
32+
send_rejection_email(
33+
organisation,
34+
'certification.qgis.org',
35+
'https'
36+
)
37+
count += 1
38+
print(f'{organisation.name} has been set to Rejected')
39+
40+
result = f'{count} certifying organisations have been set to Rejected'
41+
print('------------------------------------------------------------')
42+
print('Process finished.')
43+
return result

django_project/core/settings/base.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# Django settings for projecta project.
66
import os
77
from .utils import absolute_path
8+
from celery.schedules import crontab
89

910
ADMINS = (
1011
('Tim Sutton', 'tim@kartoza.com'),
@@ -210,4 +211,16 @@
210211
'BUNDLE_DIR_NAME': 'bundles',
211212
'STATS_FILE': os.path.join(SITE_ROOT, 'webpack-stats.json'),
212213
}
214+
}
215+
216+
CELERY_RESULT_BACKEND = 'rpc://'
217+
CELERY_BROKER_URL = os.environ.get('BROKER_URL', 'amqp://rabbitmq:5672')
218+
CELERY_BEAT_SCHEDULE = {
219+
'reject_pending_organisations': {
220+
'task': 'certification.tasks.reject_pending_organisations.reject_pending_organisations',
221+
'schedule': crontab(minute=0, hour=0), # Execute every day at midnight.
222+
'kwargs': {
223+
'days': 365, # Auto reject pending organisations that wasn't updated for a year.
224+
}
225+
},
213226
}

0 commit comments

Comments
 (0)