Mercurial > public > sg101
comparison forums/unread.py @ 581:ee87ea74d46b
For Django 1.4, rearranged project structure for new manage.py.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sat, 05 May 2012 17:10:48 -0500 |
parents | gpp/forums/unread.py@c06d836c8b84 |
children |
comparison
equal
deleted
inserted
replaced
580:c525f3e0b5d0 | 581:ee87ea74d46b |
---|---|
1 """ | |
2 This file contains routines for implementing the "has unread" feature. | |
3 Forums, topics, and posts are displayed with a visual indication if they have | |
4 been read or not. | |
5 """ | |
6 import datetime | |
7 import logging | |
8 | |
9 from django.db import IntegrityError | |
10 | |
11 from forums.models import ForumLastVisit, TopicLastVisit, Topic, Forum | |
12 | |
13 | |
14 THRESHOLD = datetime.timedelta(days=14) | |
15 | |
16 ####################################################################### | |
17 | |
18 def get_forum_unread_status(qs, user): | |
19 if not user.is_authenticated(): | |
20 for forum in qs: | |
21 forum.has_unread = False | |
22 return | |
23 | |
24 now = datetime.datetime.now() | |
25 min_date = now - THRESHOLD | |
26 | |
27 # retrieve ForumLastVisit records in one SQL query | |
28 forum_ids = [forum.id for forum in qs] | |
29 flvs = ForumLastVisit.objects.filter(user=user, | |
30 forum__in=forum_ids).select_related() | |
31 flvs = dict([(flv.forum.id, flv) for flv in flvs]) | |
32 | |
33 for forum in qs: | |
34 # Edge case: forum has no posts | |
35 if forum.last_post is None: | |
36 forum.has_unread = False | |
37 continue | |
38 | |
39 # Get the ForumLastVisit record | |
40 if forum.id in flvs: | |
41 flv = flvs[forum.id] | |
42 else: | |
43 # One doesn't exist, create a default one for next time, | |
44 # mark it as having no unread topics, and bail. | |
45 flv = ForumLastVisit(user=user, forum=forum) | |
46 flv.begin_date = now | |
47 flv.end_date = now | |
48 | |
49 # There is a race condition and sometimes another thread | |
50 # saves a record before we do; just log this if it happens. | |
51 try: | |
52 flv.save() | |
53 except IntegrityError: | |
54 logging.exception('get_forum_unread_status') | |
55 | |
56 forum.has_unread = False | |
57 continue | |
58 | |
59 # If the last visit record was too far in the past, | |
60 # catch that user up and mark as no unreads. | |
61 if now - flv.end_date > THRESHOLD: | |
62 forum.catchup(user, flv) | |
63 forum.has_unread = False | |
64 continue | |
65 | |
66 # Check the easy cases first. Check the last_post in the | |
67 # forum. If created after the end_date in our window, there | |
68 # are new posts. Likewise, if before the begin_date in our window, | |
69 # there are no new posts. | |
70 if forum.last_post.creation_date > flv.end_date: | |
71 forum.has_unread = True | |
72 elif forum.last_post.creation_date < flv.begin_date: | |
73 if not flv.is_caught_up(): | |
74 forum.catchup(user, flv) | |
75 forum.has_unread = False | |
76 else: | |
77 # Going to have to examine the topics in our window. | |
78 # First adjust our window if it is too old. | |
79 if now - flv.begin_date > THRESHOLD: | |
80 flv.begin_date = min_date | |
81 flv.save() | |
82 TopicLastVisit.objects.filter(user=user, topic__forum=forum, | |
83 last_visit__lt=min_date).delete() | |
84 | |
85 topics = Topic.objects.filter(forum=forum, | |
86 update_date__gt=flv.begin_date) | |
87 tracked_topics = TopicLastVisit.objects.filter( | |
88 user=user, | |
89 topic__forum=forum, | |
90 last_visit__gt=flv.begin_date).select_related('topic') | |
91 | |
92 # If the number of topics created since our window was started | |
93 # is greater than the tracked topic records, then there are new | |
94 # posts. | |
95 if topics.count() > tracked_topics.count(): | |
96 forum.has_unread = True | |
97 continue | |
98 | |
99 tracked_dict = dict((t.topic.id, t) for t in tracked_topics) | |
100 | |
101 for topic in topics: | |
102 if topic.id in tracked_dict: | |
103 if topic.update_date > tracked_dict[topic.id].last_visit: | |
104 forum.has_unread = True | |
105 break | |
106 else: | |
107 forum.has_unread = True | |
108 break | |
109 else: | |
110 # If we made it through the above loop without breaking out, | |
111 # then we are all caught up. | |
112 forum.catchup(user, flv) | |
113 forum.has_unread = False | |
114 | |
115 ####################################################################### | |
116 | |
117 def get_topic_unread_status(forum, topics, user): | |
118 | |
119 # Edge case: no topics | |
120 if forum.last_post is None: | |
121 return | |
122 | |
123 # This service isn't provided to unauthenticated users | |
124 if not user.is_authenticated(): | |
125 for topic in topics: | |
126 topic.has_unread = False | |
127 return | |
128 | |
129 now = datetime.datetime.now() | |
130 | |
131 # Get the ForumLastVisit record | |
132 try: | |
133 flv = ForumLastVisit.objects.get(forum=forum, user=user) | |
134 except ForumLastVisit.DoesNotExist: | |
135 # One doesn't exist, create a default one for next time, | |
136 # mark it as having no unread topics, and bail. | |
137 flv = ForumLastVisit(user=user, forum=forum) | |
138 flv.begin_date = now | |
139 flv.end_date = now | |
140 | |
141 # There is a race condition and sometimes another thread | |
142 # saves a record before we do; just log this if it happens. | |
143 try: | |
144 flv.save() | |
145 except IntegrityError: | |
146 logging.exception('get_topic_unread_status') | |
147 | |
148 for topic in topics: | |
149 topic.has_unread = False | |
150 return | |
151 | |
152 # Are all the posts before our window? If so, all have been read. | |
153 if forum.last_post.creation_date < flv.begin_date: | |
154 for topic in topics: | |
155 topic.has_unread = False | |
156 return | |
157 | |
158 topic_ids = [topic.id for topic in topics] | |
159 tlvs = TopicLastVisit.objects.filter(user=user, topic__id__in=topic_ids) | |
160 tlvs = dict([(tlv.topic.id, tlv) for tlv in tlvs]) | |
161 | |
162 # Otherwise we have to go through the topics one by one: | |
163 for topic in topics: | |
164 if topic.update_date < flv.begin_date: | |
165 topic.has_unread = False | |
166 elif topic.update_date > flv.end_date: | |
167 topic.has_unread = True | |
168 elif topic.id in tlvs: | |
169 topic.has_unread = topic.update_date > tlvs[topic.id].last_visit | |
170 else: | |
171 topic.has_unread = True | |
172 | |
173 ####################################################################### | |
174 | |
175 def get_post_unread_status(topic, posts, user): | |
176 # This service isn't provided to unauthenticated users | |
177 if not user.is_authenticated(): | |
178 for post in posts: | |
179 post.unread = False | |
180 return | |
181 | |
182 # Get the ForumLastVisit record | |
183 try: | |
184 flv = ForumLastVisit.objects.get(forum=topic.forum, user=user) | |
185 except ForumLastVisit.DoesNotExist: | |
186 # One doesn't exist, all posts are old. | |
187 for post in posts: | |
188 post.unread = False | |
189 return | |
190 | |
191 # Are all the posts before our window? If so, all have been read. | |
192 if topic.last_post.creation_date < flv.begin_date: | |
193 for post in posts: | |
194 post.unread = False | |
195 return | |
196 | |
197 # Do we have a topic last visit record for this topic? | |
198 | |
199 try: | |
200 tlv = TopicLastVisit.objects.get(user=user, topic=topic) | |
201 except TopicLastVisit.DoesNotExist: | |
202 # No we don't, we could be all caught up, or all are new | |
203 for post in posts: | |
204 post.unread = post.creation_date > flv.end_date | |
205 else: | |
206 for post in posts: | |
207 post.unread = post.creation_date > tlv.last_visit | |
208 | |
209 ####################################################################### | |
210 | |
211 def get_unread_topics(user): | |
212 """Returns a list of topics the user hasn't read yet.""" | |
213 | |
214 # This is only available to authenticated users | |
215 if not user.is_authenticated(): | |
216 return [] | |
217 | |
218 now = datetime.datetime.now() | |
219 | |
220 # Obtain list of forums the user can view | |
221 forums = Forum.objects.forums_for_user(user) | |
222 | |
223 # Get forum last visit records for the forum ids | |
224 flvs = ForumLastVisit.objects.filter(user=user, | |
225 forum__in=forums).select_related() | |
226 flvs = dict([(flv.forum.id, flv) for flv in flvs]) | |
227 | |
228 unread_topics = [] | |
229 topics = Topic.objects.none() | |
230 for forum in forums: | |
231 # if the user hasn't visited the forum, create a last | |
232 # visit record set to "now" | |
233 if not forum.id in flvs: | |
234 flv = ForumLastVisit(user=user, forum=forum, begin_date=now, | |
235 end_date=now) | |
236 flv.save() | |
237 else: | |
238 flv = flvs[forum.id] | |
239 topics |= Topic.objects.filter(forum=forum, | |
240 update_date__gt=flv.begin_date).order_by('-update_date').select_related( | |
241 'forum', 'user', 'last_post', 'last_post__user') | |
242 | |
243 if topics is not None: | |
244 # get all topic last visit records for the topics of interest | |
245 | |
246 tlvs = TopicLastVisit.objects.filter(user=user, topic__in=topics) | |
247 tlvs = dict([(tlv.topic.id, tlv) for tlv in tlvs]) | |
248 | |
249 for topic in topics: | |
250 if topic.id in tlvs: | |
251 tlv = tlvs[topic.id] | |
252 if topic.update_date > tlv.last_visit: | |
253 unread_topics.append(topic) | |
254 else: | |
255 unread_topics.append(topic) | |
256 | |
257 return unread_topics |