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