Skip to content

Commit

Permalink
caching count of inbox
Browse files Browse the repository at this point in the history
cleanup
bumped version
  • Loading branch information
Philipp Wassibauer committed Apr 15, 2012
1 parent 07491b9 commit 866fa60
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 47 deletions.
2 changes: 2 additions & 0 deletions .gitignore
@@ -0,0 +1,2 @@
*.pyc
dist/
2 changes: 1 addition & 1 deletion threaded_messages/__init__.py
@@ -1,2 +1,2 @@
VERSION = (0, 1, 28)
VERSION = (0, 1, 29)
__version__ = '.'.join(map(str, VERSION))
5 changes: 3 additions & 2 deletions threaded_messages/context_processors.py
@@ -1,7 +1,8 @@
from threaded_messages.models import inbox_count_for
from threaded_messages.models import cached_inbox_count_for


def inbox(request):
if request.user.is_authenticated():
return {'messages_inbox_count': inbox_count_for(request.user)}
return {'messages_inbox_count': cached_inbox_count_for(request.user)}
else:
return {}
14 changes: 7 additions & 7 deletions threaded_messages/forms.py
@@ -1,10 +1,7 @@
import datetime
import settings as sendgrid_settings
from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext_noop
from django.contrib.auth.models import User
from .models import *
from .fields import CommaSeparatedUserField
from .utils import reply_to_thread, now
Expand All @@ -16,14 +13,15 @@
if "notification" in settings.INSTALLED_APPS:
from notification import models as notification


class ComposeForm(forms.Form):
"""
A simple default form for private messages.
"""
recipient = CommaSeparatedUserField(label=_(u"Recipient"))
subject = forms.CharField(label=_(u"Subject"))
body = forms.CharField(label=_(u"Body"),
widget=forms.Textarea(attrs={'rows': '12', 'cols':'55'}))
widget=forms.Textarea(attrs={'rows': '12', 'cols': '55'}))

def __init__(self, *args, **kwargs):
recipient_filter = kwargs.pop('recipient_filter', None)
Expand All @@ -50,7 +48,9 @@ def save(self, sender, send=True):
sender_part.replied_at = sender_part.read_at = now()
sender_part.save()

thread.save() #save this last, since this updates the search index
thread.save() # save this last, since this updates the search index

invalidate_count_cache(new_message)

#send notifications
if send and notification:
Expand All @@ -60,8 +60,8 @@ def save(self, sender, send=True):
notification.send(recipients, "received_email",
{"thread": thread,
"message": new_message}, sender=sender,
from_email= reply_email.get_from_email(),
headers = {'Reply-To': reply_email.get_reply_to_email()})
from_email=reply_email.get_from_email(),
headers={'Reply-To': reply_email.get_reply_to_email()})
else:
notification.send(recipients, "received_email",
{"thread": thread,
Expand Down
19 changes: 13 additions & 6 deletions threaded_messages/listeners.py
@@ -1,19 +1,23 @@
from django.utils.html import strip_tags
import settings as sendgrid_settings
import logging

from django.utils.html import strip_tags

from . import settings as sendgrid_settings

logger = logging.getLogger('threaded_messages')

if sendgrid_settings.THREADED_MESSAGES_USE_SENDGRID:
from sendgrid_parse_api.signals import email_received
else:
email_received = None


def signal_received_email(sender, sma, app_id, html, text, from_field, **kwargs):
from utils import reply_to_thread, strip_mail # circular dependency fix
logger.debug("Sendgrid signal receive: %s, %s, %s, %s, %s, %s"%(sender, sma, app_id,
html, repr(text), from_field) )
from .utils import reply_to_thread, strip_mail
logger.debug("Sendgrid signal receive: %s, %s, %s, %s, %s, %s" % (sender, sma, app_id,
html, repr(text), from_field))
if app_id == sendgrid_settings.THREADED_MESSAGES_ID:
body =''
body = ''

if text:
body = text
Expand All @@ -27,7 +31,10 @@ def signal_received_email(sender, sma, app_id, html, text, from_field, **kwargs)
thread = sma.content_object
reply_to_thread(thread, sma.user, body)


def start_listening():
if email_received:
logger.debug("Sendgrid start listening")
email_received.connect(signal_received_email, dispatch_uid="thm_reply")


55 changes: 42 additions & 13 deletions threaded_messages/models.py
@@ -1,16 +1,15 @@
import datetime
from django.db import models
from django.conf import settings
from django.db.models import signals
from django.db.models.query import QuerySet
from django.contrib.auth.models import User
from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _
from django.db.models import F, Q
from django.db.models import Avg, Max, Min, Count

from .listeners import start_listening
from .settings import INBOX_COUNT_CACHE, INBOX_COUNT_CACHE_TIME

start_listening()


class MessageManager(models.Manager):

def inbox_for(self, user, read=None, only_unreplied=None):
Expand All @@ -30,12 +29,12 @@ def inbox_for(self, user, read=None, only_unreplied=None):
else:
# unread threads are the ones that either have not been read at all or before the last message arrived
inbox = inbox.filter(Q(read_at__isnull=True)
|Q(read_at__lt=F("thread__latest_msg__sent_at")))
| Q(read_at__lt=F("thread__latest_msg__sent_at")))

if only_unreplied != None:
if only_unreplied == True:
inbox = inbox.filter(Q(replied_at__isnull=True)
|Q(replied_at__lt=F("thread__latest_msg__sent_at")))
| Q(replied_at__lt=F("thread__latest_msg__sent_at")))

return inbox

Expand Down Expand Up @@ -153,21 +152,33 @@ def others(self):

def get_next(self):
try:
participation = Participant.objects.inbox_for(self.user).filter(thread__latest_msg__sent_at__gt=\
self.thread.latest_msg.sent_at).reverse()[0]
participation = Participant.objects.inbox_for(
self.user
).filter(
thread__latest_msg__sent_at__gt=self.thread.latest_msg.sent_at
).reverse()[0]
return participation
except:
return None


def get_previous(self):
try:
participation = Participant.objects.inbox_for(self.user).filter(thread__latest_msg__sent_at__lt=\
self.thread.latest_msg.sent_at)[0]
participation = Participant.objects.inbox_for(
self.user
).filter(
thread__latest_msg__sent_at__lt=self.thread.latest_msg.sent_at)[0]
return participation
except:
return None

def read_thread(self):
"""
Marks thread as read and invalidates count cache
"""
from .utils import fill_count_cache, now
self.read_at = now()
fill_count_cache(self.user)

def __unicode__(self):
return "%s - %s" % (str(self.user), self.thread.subject)

Expand All @@ -177,10 +188,28 @@ class Meta:
verbose_name_plural = _("participants")


def inbox_count_for(user):
def cached_inbox_count_for(user):
"""
returns the number of unread messages for the given user but does not
mark them seen
"""
count = cache.get(INBOX_COUNT_CACHE % user.pk)
if count:
return count
else:
count = inbox_count_for(user)
cache.set(INBOX_COUNT_CACHE % user.pk,
count,
INBOX_COUNT_CACHE_TIME)
return count


def inbox_count_for(user):
return Participant.objects.inbox_for(user, read=False).count()


def invalidate_count_cache(message):
from .utils import fill_count_cache
for thread in message.thread.select_related().all():
for participant in thread.participants.exclude(user=message.sender):
fill_count_cache(participant.user)
5 changes: 4 additions & 1 deletion threaded_messages/settings.py
@@ -1,4 +1,7 @@
from django.conf import settings

THREADED_MESSAGES_USE_SENDGRID = getattr(settings, 'THREADED_MESSAGES_USE_SENDGRID', False)
THREADED_MESSAGES_ID = getattr(settings, 'THREADED_MESSAGES_ID', 'm')
THREADED_MESSAGES_ID = getattr(settings, 'THREADED_MESSAGES_ID', 'm')

INBOX_COUNT_CACHE = "THREADED_MESSAGES_INBOX_COUNT_%s"
INBOX_COUNT_CACHE_TIME = 60 * 60 * 6
7 changes: 5 additions & 2 deletions threaded_messages/templatetags/inbox.py
@@ -1,5 +1,6 @@
from django.template import Library, Node, TemplateSyntaxError
from threaded_messages.models import inbox_count_for
from threaded_messages.models import cached_inbox_count_for


class InboxOutput(Node):
def __init__(self, varname=None):
Expand All @@ -8,15 +9,17 @@ def __init__(self, varname=None):
def render(self, context):
try:
user = context['user']
count = inbox_count_for(user)
count = cached_inbox_count_for(user)
except (KeyError, AttributeError):
count = ''

if self.varname is not None:
context[self.varname] = count
return ""
else:
return "%s" % (count)


def do_print_inbox_count(parser, token):
"""
A templatetag to show the unread-count for a logged in user.
Expand Down
23 changes: 13 additions & 10 deletions threaded_messages/utils.py
Expand Up @@ -2,18 +2,14 @@
import HTMLParser
import datetime
import re
from lxml.html.clean import Cleaner

from django.conf import settings
from django.contrib.sites.models import Site
from django.utils.encoding import force_unicode
from django.utils.text import wrap
from django.utils.translation import ugettext_lazy as _
from django.template import Context, loader
from django.template.loader import render_to_string, get_template
from django.core.cache import cache
from django.template import Context
from django.template.loader import get_template

import settings as tm_settings
from .models import Message, Participant
from . import settings as tm_settings
from .models import Message, Participant, inbox_count_for, invalidate_count_cache


if "notification" in settings.INSTALLED_APPS:
Expand All @@ -34,6 +30,11 @@
now = datetime.datetime.now


def fill_count_cache(user):
cache.set(tm_settings.INBOX_COUNT_CACHE % user.pk,
inbox_count_for(user), tm_settings.INBOX_COUNT_CACHE_TIME)


def open_message_thread(recipients, subject, template,
sender, context={}, send=True, message=None):
body = ''
Expand Down Expand Up @@ -75,6 +76,8 @@ def reply_to_thread(thread,sender, body):
sender_part.replied_at = sender_part.read_at = now()
sender_part.save()

invalidate_count_cache(new_message)

if notification:
for r in recipients:
if tm_settings.THREADED_MESSAGES_USE_SENDGRID:
Expand All @@ -83,7 +86,7 @@ def reply_to_thread(thread,sender, body):
{"thread": thread,
"message": new_message}, sender=sender,
from_email=reply_email.get_from_email(),
headers = {'Reply-To': reply_email.get_reply_to_email()})
headers={'Reply-To': reply_email.get_reply_to_email()})
else:
notification.send([r], "received_email",
{"thread": thread,
Expand Down
11 changes: 6 additions & 5 deletions threaded_messages/views.py
Expand Up @@ -79,6 +79,7 @@ def outbox(request, template_name='django_messages/inbox.html'):
'thread_list': thread_list,
}, context_instance=RequestContext(request))


@login_required
def trash(request, template_name='django_messages/trash.html'):
"""
Expand All @@ -93,6 +94,7 @@ def trash(request, template_name='django_messages/trash.html'):
'message_list': message_list,
}, context_instance=RequestContext(request))


@login_required
def compose(request, recipient=None, form_class=ComposeForm,
template_name='django_messages/compose.html', success_url=None, recipient_filter=None):
Expand Down Expand Up @@ -207,7 +209,6 @@ def view(request, thread_id, form_class=ReplyForm,
else:
form = form_class()

right_now = now()
participant = get_object_or_404(Participant, thread=thread, user=request.user)
message_list = []
# in this view we want the last message last
Expand All @@ -216,7 +217,7 @@ def view(request, thread_id, form_class=ReplyForm,
if participant.read_at and message.sent_at <= participant.read_at:
unread = False
message_list.append((message, unread,))
participant.read_at = right_now
participant.read_thread()
participant.save()
return render_to_response(template_name, {
'thread': thread,
Expand Down Expand Up @@ -290,9 +291,9 @@ def message_ajax_reply(request, thread_id,
@login_required
def recipient_search(request):
term = request.GET.get("term")
users = User.objects.filter(Q(first_name__icontains=term)|
Q(last_name__icontains=term)|
Q(username__icontains=term)|
users = User.objects.filter(Q(first_name__icontains=term) |
Q(last_name__icontains=term) |
Q(username__icontains=term) |
Q(email__icontains=term))
if request.GET.get("format") == "json":
data = []
Expand Down

0 comments on commit 866fa60

Please sign in to comment.