bgneal@509
|
1 """
|
bgneal@509
|
2 This module maintains the latest posts datastore. The latest posts are often
|
bgneal@509
|
3 needed by RSS feeds, "latest posts" template tags, etc. This module listens for
|
bgneal@509
|
4 the post_content_update signal, then bundles the post up and stores it by forum
|
bgneal@509
|
5 ID in Redis. We also maintain a combined forums list. This allows quick
|
bgneal@509
|
6 retrieval of the latest posts and avoids some slow SQL queries.
|
bgneal@509
|
7
|
bgneal@509
|
8 """
|
bgneal@509
|
9 import datetime
|
bgneal@509
|
10 import time
|
bgneal@509
|
11
|
bgneal@509
|
12 from django.dispatch import receiver
|
bgneal@509
|
13 from django.utils import simplejson
|
bgneal@509
|
14
|
bgneal@509
|
15 from forums.signals import post_content_update
|
bgneal@509
|
16 from forums.models import Forum
|
bgneal@509
|
17 from core.services import get_redis_connection
|
bgneal@509
|
18
|
bgneal@509
|
19
|
bgneal@509
|
20 # This constant controls how many latest posts per forum we store
|
bgneal@509
|
21 MAX_POSTS = 50
|
bgneal@509
|
22
|
bgneal@509
|
23
|
bgneal@509
|
24 @receiver(post_content_update, dispatch_uid='forums.latest_posts')
|
bgneal@509
|
25 def on_post_update(sender, **kwargs):
|
bgneal@509
|
26 """
|
bgneal@509
|
27 This function is our signal handler, called when a post has been updated.
|
bgneal@509
|
28 We only care about newly created posts, and ignore updates.
|
bgneal@509
|
29
|
bgneal@509
|
30 We serialize a post to JSON then store in two lists in Redis:
|
bgneal@509
|
31 1. The list for the post's parent forum
|
bgneal@509
|
32 2. The combined forum list
|
bgneal@509
|
33
|
bgneal@509
|
34 Note that we only store posts from public forums.
|
bgneal@509
|
35
|
bgneal@509
|
36 """
|
bgneal@509
|
37 # ignore non-new posts
|
bgneal@509
|
38 if not kwargs['created']:
|
bgneal@509
|
39 return
|
bgneal@509
|
40
|
bgneal@509
|
41 # ignore posts from non-public forums
|
bgneal@509
|
42 public_forums = Forum.objects.public_forum_ids()
|
bgneal@509
|
43 if sender.topic.forum.id not in public_forums:
|
bgneal@509
|
44 return
|
bgneal@509
|
45
|
bgneal@509
|
46 # serialize post attributes
|
bgneal@509
|
47 post_content = {
|
bgneal@509
|
48 'id': sender.id,
|
bgneal@509
|
49 'title': sender.topic.name,
|
bgneal@509
|
50 'content': sender.html,
|
bgneal@509
|
51 'author': sender.user.username,
|
bgneal@509
|
52 'pubdate': int(time.mktime(sender.creation_date.timetuple())),
|
bgneal@509
|
53 'forum_name': sender.topic.forum.name,
|
bgneal@509
|
54 'url': sender.get_absolute_url()
|
bgneal@509
|
55 }
|
bgneal@509
|
56
|
bgneal@509
|
57 s = simplejson.dumps(post_content)
|
bgneal@509
|
58
|
bgneal@509
|
59 # store in Redis
|
bgneal@509
|
60
|
bgneal@509
|
61 redis = get_redis_connection()
|
bgneal@509
|
62 pipeline = redis.pipeline()
|
bgneal@509
|
63
|
bgneal@509
|
64 key = 'forums:latest:%d' % sender.topic.forum.id
|
bgneal@509
|
65
|
bgneal@509
|
66 pipeline.lpush(key, s)
|
bgneal@509
|
67 pipeline.ltrim(key, 0, MAX_POSTS - 1)
|
bgneal@509
|
68
|
bgneal@509
|
69 # store in the combined feed; yes this wastes some memory storing it twice,
|
bgneal@509
|
70 # but it makes things much easier
|
bgneal@509
|
71
|
bgneal@509
|
72 key = 'forums:latest:*'
|
bgneal@509
|
73
|
bgneal@509
|
74 pipeline.lpush(key, s)
|
bgneal@509
|
75 pipeline.ltrim(key, 0, MAX_POSTS - 1)
|
bgneal@509
|
76
|
bgneal@509
|
77 pipeline.execute()
|
bgneal@509
|
78
|
bgneal@509
|
79
|
bgneal@509
|
80 def get_latest_posts(num_posts=MAX_POSTS, forum_id=None):
|
bgneal@509
|
81 """
|
bgneal@509
|
82 This function retrieves num_posts latest posts for the forum with the given
|
bgneal@509
|
83 forum_id. If forum_id is None, the posts are retrieved from the combined
|
bgneal@509
|
84 forums datastore. A list of dictionaries is returned. Each dictionary
|
bgneal@509
|
85 contains information about a post.
|
bgneal@509
|
86
|
bgneal@509
|
87 """
|
bgneal@509
|
88 key = 'forums:latest:%d' % forum_id if forum_id else 'forums:latest:*'
|
bgneal@509
|
89
|
bgneal@509
|
90 num_posts = max(0, min(MAX_POSTS, num_posts))
|
bgneal@509
|
91
|
bgneal@509
|
92 if num_posts == 0:
|
bgneal@509
|
93 return []
|
bgneal@509
|
94
|
bgneal@509
|
95 redis = get_redis_connection()
|
bgneal@509
|
96 raw_posts = redis.lrange(key, 0, num_posts - 1)
|
bgneal@509
|
97
|
bgneal@509
|
98 posts = []
|
bgneal@509
|
99 for raw_post in raw_posts:
|
bgneal@509
|
100 post = simplejson.loads(raw_post)
|
bgneal@509
|
101
|
bgneal@509
|
102 # fix up the pubdate; turn it back into a datetime object
|
bgneal@509
|
103 post['pubdate'] = datetime.datetime.fromtimestamp(post['pubdate'])
|
bgneal@509
|
104
|
bgneal@509
|
105 posts.append(post)
|
bgneal@509
|
106
|
bgneal@509
|
107 return posts
|