I've created a template in Django which has as function to wish a happy birthday to a client. I'd like to set that message in such a way it'd be send to the client every year for his birthday. I think the best way to do this is to create a cron job. However, I am not familiar with cron jobs, and I would like your help.
I've created an attribute birthday_date that will give us the birthday date as day month. Here's what I've done so far :
#!/bin/bash
MANAGE="../venv/bin/python ../manage.py"
Could anyone be able to tell me how could I do this?
Thanks in advance!
A simple solution would be to create a custom management command which will send the happy birthday emails of the day and run it every day via a cronjob.
This an example custom command, yours will probably be different, depending on how you store your user data:
# app/management/commands/send_daily_birthday_greetings.py
"""
Custom management command that sends an email to all users
born on the current day and month.
"""
from django.core.management import BaseCommand
from django.core.mail import send_mail
from django.utils import timezone
from someplace import User
class Command(BaseCommand):
def handle(self, **options):
today = timezone.now().date()
for user in User.objects.filter(birth_date__day=today.day, birth_date__month=today.month):
subject = 'Happy birthday %s!' % user.first_name
body = 'Hi %s,\n...' + user.first_name
send_mail(subject, body, 'contact#yourdomain.com', [user.email])
Then edit your crontab configuration with crontab -e this way:
# m h dom mon dow command
0 10 * * * /path/to/your/python/executable/python /path/to/your/manage.py send_daily_birthday_greetings
This will send the emails at 10h00 every day, you may change the time as you wish.
In building on aumo's answer, if you're deploying to a PaaS, like Heroku for instance, and cannot schedule cron jobs, you can use a combination of honcho and management commands to schedule your periodic tasks. Sometimes it can also be nice to keep it all a part of the application rather than having to edit other system files like cron. honcho
For example, your Procfile might look like this:
web: gunicorn config.wsgi:application
tasks: honcho -f ProcfileHoncho start
Your honcho file might look like this:
clock: python clock.py
reports: python app_name/tasks/reports.py
And your clock file calls the management commands:
import os
import subprocess
from apscheduler.schedulers.blocking import BlockingScheduler
sched = BlockingScheduler()
# Morning updates. Times in UTC time.
#sched.scheduled_job('cron', hour=11)
def birthdays():
os.system('python manage.py send_daily_birthday_greetings')
Related
Celery has documentation about Periodic Task, it has shown couple of example how can we add period task. For example:
from celery import Celery
from celery.schedules import crontab
app = Celery()
#app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
# Calls test('hello') every 10 seconds.
sender.add_periodic_task(10.0, test.s('hello'), name='add every 10')
This will set periodic task at initialization. However, I would like to add period task based user's activity e.g. when user visit task page in Flask view. I have flask view like:
#app.route('/task', methods=['GET'])
def payload_mobile_android():
return {'task': 'mytask'}, 200
Thanks for help in advance.
In my django application I am using celery. In a post_save signal, I am updating the index in elastic search. But for some reason the task gets hung and never actually executes the code:
What I use to run celery:
celery -A collegeapp worker -l info
The Signal:
#receiver(post_save, sender=University)
def university_saved(sender, instance, created, **kwargs):
"""
University save signal
"""
print('calling celery task')
update_university_index.delay(instance.id)
print('finished')
The task:
#task(name="update_university_index", bind=True, default_retry_delay=5, max_retries=1, acks_late=True)
def update_university_index(instance_id):
print('updating university index')
The only output I get is calling celery task. after waiting over 30 minutes, it doesn't ever get to any other print statements and the view continue to wait. Nothing ever shows in celery terminal.
Versions:
Django 3.0,
Celery 4.3,
Redis 5.0.9,
Ubuntu 18
UPDATE:
after doing some testing, using the debug_task defined inside the celery.py file in place of update_university_index does not lead to hanging. It behaves as expect. I thought maybe it could have been app.task vs task decorator but it seems that's not it.
#app.task(bind=True)
def debug_task(text, second_value):
print('printing debug_task {} {}'.format(text, second_value))
This happened with me once, I had made the dumbest error, django tells us to specify celery tasks in tasks.py file, and uses that for task discovery. After that it worked. Could you provide more insight into the directory structure using tree command?
This tutorial is for flask, but the same can be achieved in django. Where this particular tutorial shines is that after you tell celery to execute a task, it also provides you with a uuid and you can ping that url and monitor the progress of the task you triggered.
Verify that the tasks have been registered by celery using (Do make sure that celery is running):
from celery.task.control import inspect
i = inspect()
i.registered_tasks()
Or bash
$ celery inspect registered
$ celery -A collegeapp inspect registered
From https://docs.celeryproject.org/en/latest/faq.html#the-worker-isn-t-doing-anything-just-hanging
Why is Task.delay/apply*/the worker just hanging?
Answer: There’s a bug in some AMQP clients that’ll make it hang if it’s not able to authenticate the current user, the password doesn’t match or the user doesn’t have access to the virtual host specified. Be sure to check your broker logs (for RabbitMQ that’s /var/log/rabbitmq/rabbit.log on most systems), it usually contains a message describing the reason.
Change this line
#task(name="update_university_index", bind=True, default_retry_delay=5, max_retries=1, acks_late=True)
def update_university_index(instance_id):
print('updating university index')
To
#task(name="update_university_index", bind=True, default_retry_delay=5, max_retries=1, acks_late=True)
def update_university_index(self, instance_id):
print('updating university index')
Or add self to the task definition.
I'm still not sure as to why it doesn't work but I found a solution by replace task with app.task
importing app from my celery.py seemed to have resolved the issue.
from collegeapp.celery import app
#app.task(name="update_university_index", bind=True, default_retry_delay=5, max_retries=1, acks_late=True)
def update_university_index(self, instance_id):
print('updating university index')
Is there an easy way to be able to send an email at a later time, say Aug 1, 2012 6 pm? I have tried to read some documentation on django-mailer, but I could not get to an answer.
I am starting out in web development so may not be able to hack the existing application of django-mailer to get this done.
Celery can fit your need.
First set up a celery task:
#task
def sendmail():
pass
Send a mail later, an example from the doc:
from datetime import datetime, timedelta
tomorrow = datetime.now() + timedelta(days=1)
sendmail.apply_async(args=[], eta=tomorrow)
I use:
Celery
Django-Celery
RabbitMQ
I can see all my tasks in the Django admin page, but at the moment it has just a few states, like:
RECEIVED
RETRY
REVOKED
SUCCESS
STARTED
FAILURE
PENDING
It's not enough information for me. Is it possible to add more details about a running process to the admin page? Like progress bar or finished jobs counter etc.
I know how to use the Celery logging function, but a GUI is better in my case for some reasons.
So, is it possible to send some tracing information to the Django-Celery admin page?
Here's my minimal progress-reporting Django backend using your setup. I'm still a Django n00b and it's the first time I'm messing with Celery, so this can probably be optimized.
from time import sleep
from celery import task, current_task
from celery.result import AsyncResult
from django.http import HttpResponse, HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.utils import simplejson as json
from django.conf.urls import patterns, url
#task()
def do_work():
""" Get some rest, asynchronously, and update the state all the time """
for i in range(100):
sleep(0.1)
current_task.update_state(state='PROGRESS',
meta={'current': i, 'total': 100})
def poll_state(request):
""" A view to report the progress to the user """
if 'job' in request.GET:
job_id = request.GET['job']
else:
return HttpResponse('No job id given.')
job = AsyncResult(job_id)
data = job.result or job.state
return HttpResponse(json.dumps(data), mimetype='application/json')
def init_work(request):
""" A view to start a background job and redirect to the status page """
job = do_work.delay()
return HttpResponseRedirect(reverse('poll_state') + '?job=' + job.id)
urlpatterns = patterns('webapp.modules.asynctasks.progress_bar_demo',
url(r'^init_work$', init_work),
url(r'^poll_state$', poll_state, name="poll_state"),
)
I am starting to try figuring this out myself. Start by defining a PROGRESS state exactly as explained on the Celery userguide, then all you need is to insert a js in your template that will update your progress bar.
Thank #Florian Sesser for your example!
I made a complete Django app that show the progress of create 1000 objects to the users at http://iambusychangingtheworld.blogspot.com/2013/07/django-celery-display-progress-bar-of.html
Everyone can download and use it!
I would recommend a library called celery-progress for this. It is designed to make it as easy as possible to drop-in a basic end-to-end progress bar setup into a django app with as little scaffolding as possible, while also supporting heavy customization on the front-end if desired. Lots of docs and references for getting started in the README.
Full disclosure: I am the author/maintainer of said library.
Is there an existing plug-in to produce daily or weekly digest emails in Django? (We want to combine many small notifications into one email, rather than bother people all the time.)
Django-mailer claims to support this, but I'm told it doesn't really.
There is django-mailer app of which I was not aware till now, so the answer below details my own approach.
The simplest case won't require much:
put this into your app/management/commands/send_email_alerts.py, then set up a cron job to run this command once a week with python manage.py send_email_alerts (all paths must be set in the environment of course for manage.py to pick up your app settings)
from django.core.management.base import NoArgsCommand
from django.db import connection
from django.core.mail import EmailMessage
class Command(NoArgsCommand):
def handle_noargs(self,**options):
try:
self.send_email_alerts()
except Exception, e:
print e
finally:
connection.close()
def send_email_alerts(self):
for user in User.objects.all():
text = 'Hi %s, here the news' % user.username
subject = 'some subject'
msg = EmailMessage(subject, text, settings.DEFAULT_FROM_EMAIL, [user.email])
msg.send()
But if you will need to keep track of what to email each user and how often, some extra code will be needed. Here is a homegrown example. Maybe that's where django-mailer can fill in the gaps.
I've just released the django-digested package to PyPI. It supports instant notifications, daily and weekly digests, and individual preferences for different groups of updates.