deferred email sending in django? - django

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)

Related

Calling a django function periodically

This is a rookie question on web development. I am trying to find a secure and better way for developers to call and run a function that sends emails out from my django application that they can override and send manually as well as also can be time activated thus sending the email periodically at fixed times of the week.
I found answer suggesting the use of celery on Running a function periodically in Django
But I want to change the periods without redeploying my application. I have done some research on aws tools and I think a combination of AWS gateway and aws lambda and aws cloudwatch to send a url/endpoint (or get request) to my web app to activate the function.
At the moment I have something like below.
views.py
def api_send_email(request):
#insert send email function
print ("sending")
return redirect('/')
urls.py
urlpatterns = [
url(r'^send_email$', api_send_email, name="api_send_email"),
]
So the above can either manually be triggered by going to the url https//xxx/send_email or by is sending a get request to that url periodically from aws. I have thought about doing a post request instead which will make it more secure but I am not sure if the aws tools can do that because it requires the csrf token in my app itself.
Any suggestions on what is the best way to be doing this is welcome.
Thank you
I think you can accomplish this with celery as well. For that, you can add a periodic task. Lets say you have a periodic task which initiates every 5 minutes.
Then you can have your logic in a Model to determine if it should be sent the email at that time. For example:
class YourTaskConfig(models.Model):
SEND_CHOICES = (
('minute': 'minute'),
('hour': 'hour'),
('day': 'day'),
)
send_every = models.CharField(max_length=25, choices=SEND_CHOICES)
interval_amount = models.IntegerField()
last_executed = models.DateTimeField(auto_add_now=True)
is_active = models.BooleanField(default=True)
def should_run(self):
now = timezone.now()
if self.send_every == 'minute':
td = datetime.timedelta(seconds=self.interval_amount*60)
elif self.send_every == 'day':
td = datetime.timedelta(days=self.interval_amount)
... # rest of the logic on time delta
if now - self.last_executed >= td:
self.save() # Updates current execution time
return True
return False
Your Email model can have a FK to this configuration(if you have one):
class Email(models):
config = models.ForeignKey(YourTaskConfig, on_delete=models.DO_NOTHING)
And use it periodic task:
from celery.task.schedules import crontab
from celery.decorators import periodic_task
#periodic_task(run_every=(crontab(minute='*/5')), name="some_task", ignore_result=True) # Runs every 5 minute
def some_task():
for i in YourTaskConfig.objects.filter(is_active=True): # run only active tasks
should_send_email = i.should_run()
if should_send_email:
i.email_set.all() # Here you go, you have your emails which you want send
FYI: Its an untested code, but you can get the general idea behind this solution. Hope it helps!!

Django template - Create a cron job to wish an happy birthday

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')

Django: Limit user's session by time

I'm working on the app where I need to limit the ability to log in and be authenticated for a specified time of the day. Let's say from 8am to 5pm. To limit the ability to log in I created a custom auth backend where authenticate() method returns user object only if current time is within allowed period of time.
Now I want to terminate user's auth session after specified time.
Setting session expiry_date date and cookie's Expiry seems to be the best way to achieve this, but after reading Django docs and digging in the source code for some time I did not found a good solution to it. How do I do this? Any help is appreciated.
Changing the auth backend is probably not the solution you are looking for (at least I wouldn't recommend it), since you are changing security-critical parts of your application.
I would suggest a custom middleware: If registered users trying to access your site between 8am and 5pm, they'll see a warning that this site cannot be used.
from django.utils import timezone
from django.core.exceptions import PermissionDenied
class AccessRestrictionMiddleware:
def process_request(self, request):
current_hour = timezone.now().hour
is_time_restricted = current_hour >= 8 and current_hour < 17
if request.user.is_authenticated() and is_time_restricted:
raise PermissionDenied

Django-Celery progress bar

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.

Django email digest

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.