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.
Related
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')
Is there any way to see if a user is inactive for a certain amount of time? For example Twitter sends out an email to it's users after a certain amount of time of inactivity. I would like to implement a similar feature where if a user has been inactive for 30 days, an email is sent "Hello User, check out what your friends having been posting" How can I implement this?
Well, django.contrib.auth.models.User has a last_login field which might be useful for you.
Just wherever you want, check the last_login date of the User and you'll now how long he's been away of your site.
Hope this helps!
You can write a management command which checks for the last time user logged in and if the days are greater than 30, send an email. (You could implement this as a cron that runs every day)
import datetime
from django.core.management.base import BaseCommand
def compute_inactivity():
inactive_users = User.objects.filter(last_login__lt=datetime.datetime.now() - datetime.timedelta(months=1))
#send out emails to these users
class Command(BaseCommand):
def handle(self, **options):
compute_inactivity()
If you have any other criteria which defines "activity", you can filter your queryset based on that.
After reading karthikr's answer and Aidas Bendoraitis' suggestion, I've written the correction solution below. It is very similar to Karthikr's answer except instead of using the __lt rich comparison operator, use the __eq operator:
import datetime
from django.core.management.base import BaseCommand
def compute_inactivity():
inactive_users = User.objects.filter(last_login__eq=datetime.datetime.now() - datetime.timedelta(months=1))
#send out emails to these users
class Command(BaseCommand):
def handle(self, **options):
compute_inactivity()
My approach would be to send notifications to users exactly when 30 days pass since their last login. For this, you will need to create a management command and run it as a cron job daily.
import datetime
from django.core.management.base import BaseCommand
def compute_inactivity():
a_month_ago = datetime.datetime.now() - datetime.timedelta(days=30)
inactive_users = User.objects.filter(
last_login__year=a_month_ago.year,
last_login__month=a_month_ago.month,
last_login__day=a_month_ago.day,
)
#send out emails to these users
class Command(BaseCommand):
def handle(self, **options):
compute_inactivity()
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.
I want to send an email with login details to user emailaddress whenever Admin adds new user to admin site.
I know Django provides send_mail module for that,but I don't know where should I put this code
and override some view to send automatic mails on new user addition.
from django.core.mail import send_mail
send_mail('Subject here', 'Here is the message.', 'from#example.com',
['to#example.com'], fail_silently=False)
How can i do it?
I tried putting this code in my models.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User
def email_new_user(sender, **kwargs):
if kwargs["created"]: # only for new users
new_user = kwargs["instance"]
print new_user.email
#send_mail('Subject here', 'Here is the message.', 'from#example.com',['to#example.com'], fail_silently=False)
post_save.connect(email_new_user, sender=User)
But its not printing anything on command line. I think the function is not getting called whenever i create a new user.Don't know why?
There are multiple problems I can see in the code as I can see it, above.
First, the def email_new_user() is wrongly indented. If that is not a formatting error here, correct it.
new_user = kwargs.["instance"] is wrong syntax. It really should be kwargs["instance"]
And then, do you have an SMTP server running? Can you send email from the shell? If not, configure that and then try it again. It should work.
You want to hook the post_save signal for the User model.
One possible problem was, Django user cretion consists of two steps. First django asks for username, password and password confirmation, and when you press save (or save and continue edit) your save method fired without e-mail information. Since django will accept that request as a newly created record, your signal will fire for a record with no e-mail information...
If django handles admin save diffrently and do not mark a newly created record instance as created you shall not have any problem, but probably django will not do that.
UPDATE: Some possible reasons are:
You need django 1.3, so check your django version with python manage.py version and update it if required...
Best place for your your models.py files
You can put signal handling and registration code anywhere you like. However, you'll need to make sure that the module it's in gets imported early on so that the signal handling gets registered before any signals need to be sent. This makes your app's models.py a good place to put registration of signal handlers.
These are basic problems...
I want to substitute build-in send_mail function, that works only with plain-text emails, with my own smart send_mail function, that generates both html and plain-text versions automatically. Everything works as expected for my own emails, defined in my own application. I can do it in views.py in this fashion:
from django.core import mail
from utils import send_mail as send_html_mail
mail.send_mail = send_html_mail
But the problem still appears with emails of third-side applications. There all imports already completed before my code with monkey-patching of send_mail function.
Is it possible to override this function before all imports of all django applications? Or may be there is another solution of this issue. I really don't want to patch code with sending emails of these third-party applications. It's much easy just to put html-template.
Django 1.2 allows for pluggable email backends: http://docs.djangoproject.com/en/1.2/topics/email/#defining-a-custom-e-mail-backend
From Django docs: http://docs.djangoproject.com/en/dev/topics/email/#sending-alternative-content-types
from django.core.mail import EmailMultiAlternatives
subject, from_email, to = 'hello', 'from#example.com', 'to#example.com'
text_content = 'This is an important message.'
html_content = '<p>This is an <strong>important</strong> message.</p>'
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.send()
No send_mail() required.
Sounds like a lot of people are misreading your question.
To override the import from third party modules, overwriting sys.modules['django.core.mail'] might work.
It works in simple tests, but it's not thoroughly tested.
import sys
from django.core import mail
def my_send_mail(*args, **kwargs):
print "Sent!11"
mail.send_mail = my_send_mail
sys.modules['django.core.mail'] = mail
from django.core.mail import send_mail
send_mail('blah')
# output: Sent!11
For example, if I put that code in my settings.py and launch the manage.py shell and import send_mail, I get my version.
My shell session
In [1]: from django.core.mail import send_mail
In [2]: send_mail()
Sent!11
Disclaimer
I've never done anything like this and don't know if there are any funky ramifications.
I wrote application for quickly switching from plain-text emails to html based emails for all project's applications. It will work without modifying any Django code or code of other third-part applications.
Check it out here: https://github.com/ramusus/django-email-html
How about changing django a little and substituting original code with your own function? You can actually just add your own function and don't remove the old, as the last definition will be imported. I don't like such solutions, because I don't like patching external libraries (you need to remember about patching them again when you upgrade), but this would solve your problem quickly.
In Django 1.7 added extra attribute html_message to send_mail() function to send html message.
https://docs.djangoproject.com/en/1.8/topics/email/#send-mail