Skip to content

Commit

Permalink
Added 'last_post_on' dates on Forum and Topic models
Browse files Browse the repository at this point in the history
  • Loading branch information
ellmetha committed Mar 7, 2015
1 parent b02bc6e commit 088cc76
Show file tree
Hide file tree
Showing 12 changed files with 37 additions and 38 deletions.
12 changes: 5 additions & 7 deletions machina/apps/forum/abstract_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class AbstractForum(MPTTModel, ActiveModel, DatedModel):
topics_count = models.PositiveIntegerField(verbose_name=_('Number of topics'), editable=False, blank=True, default=0)
link_redirects_count = models.PositiveIntegerField(verbose_name=_('Track link redirects count'),
editable=False, blank=True, default=0)
last_post_on = models.DateTimeField(verbose_name=_('Last post added on'), blank=True, null=True)

# Display options
display_sub_forum_list = models.BooleanField(verbose_name=_('Display in parent-forums legend'),
Expand Down Expand Up @@ -175,12 +176,8 @@ def _simple_save(self, *args, **kwargs):
This allow the database to not be hit by such checks during very common and regular
operations such as those provided by the update_trackers function; indeed these operations
will never result in an update of a forum parent.
This save is done without triggering the update of the 'updated' field by disabling the
'auto_now' behavior.
"""
self._meta.get_field_by_name('updated')[0].auto_now = False
super(AbstractForum, self).save(*args, **kwargs)
self._meta.get_field_by_name('updated')[0].auto_now = True

def update_trackers(self):
# Fetch the list of ids of all descendant forums including the current one
Expand All @@ -190,16 +187,17 @@ def update_trackers(self):
# associated with the current forum plus the list of all topics associated
# with the descendant forums.
topic_klass = models.get_model('forum_conversation', 'Topic')
topics = topic_klass.objects.filter(forum__id__in=forum_ids).order_by('-updated')
topics = topic_klass.objects.filter(forum__id__in=forum_ids).order_by('-last_post_on')
approved_topics = topics.filter(approved=True)

self.topics_count = approved_topics.count()
# Compute the forum level posts count (only approved posts are recorded)
posts_count = sum(topic.posts_count for topic in topics)
self.posts_count = posts_count

# Force the forum 'updated' date to the one associated with the last topic updated
self.updated = approved_topics[0].updated if len(approved_topics) else now()
# Force the forum 'last_post_on' date to the one associated with the topic with
# the latest post.
self.last_post_on = approved_topics[0].last_post_on if len(approved_topics) else now()

# Any save of a forum triggered from the update_tracker process will not result
# in checking for a change of the forum's parent.
Expand Down
1 change: 1 addition & 0 deletions machina/apps/forum/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Migration(migrations.Migration):
('posts_count', models.PositiveIntegerField(default=0, verbose_name='Number of posts', editable=False, blank=True)),
('topics_count', models.PositiveIntegerField(default=0, verbose_name='Number of topics', editable=False, blank=True)),
('link_redirects_count', models.PositiveIntegerField(default=0, verbose_name='Track link redirects count', editable=False, blank=True)),
('last_post_on', models.DateTimeField(null=True, verbose_name='Last post added on', blank=True)),
('display_sub_forum_list', models.BooleanField(default=True, help_text='Displays this forum on the legend of its parent-forum (sub forums list)', verbose_name='Display in parent-forums legend')),
('lft', models.PositiveIntegerField(editable=False, db_index=True)),
('rght', models.PositiveIntegerField(editable=False, db_index=True)),
Expand Down
13 changes: 6 additions & 7 deletions machina/apps/forum_conversation/abstract_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ class AbstractTopic(DatedModel):
# The number of time the topic has been viewed
views_count = models.PositiveIntegerField(verbose_name=_('Views count'), editable=False, blank=True, default=0)

# The date of the latest post
last_post_on = models.DateTimeField(verbose_name=_('Last post added on'), blank=True, null=True)

# Many users can subscribe to this topic
subscribers = models.ManyToManyField(AUTH_USER_MODEL, related_name='subscriptions', verbose_name=_('Subscribers'), blank=True, null=True)

Expand All @@ -75,8 +78,8 @@ class AbstractTopic(DatedModel):
class Meta:
abstract = True
app_label = 'forum_conversation'
ordering = ['-type', '-updated', ]
get_latest_by = 'updated'
ordering = ['-type', '-last_post_on', ]
get_latest_by = 'last_post_on'
verbose_name = _('Topic')
verbose_name_plural = _('Topics')

Expand Down Expand Up @@ -154,12 +157,8 @@ def _simple_save(self, *args, **kwargs):
This allow the database to not be hit by such checks during very common and regular
operations such as those provided by the update_trackers function; indeed these operations
will never result in an update of a topic's forum.
This save is done without triggering the update of the 'updated' field by disabling the
'auto_now' behavior.
"""
self._meta.get_field_by_name('updated')[0].auto_now = False
super(AbstractTopic, self).save(*args, **kwargs)
self._meta.get_field_by_name('updated')[0].auto_now = True

def delete(self, using=None):
super(AbstractTopic, self).delete(using)
Expand All @@ -173,7 +172,7 @@ def update_trackers(self):
self.posts_count = self.posts.filter(approved=True).count()
posts = self.posts.all().order_by('-created')
self._last_post = posts[0] if posts.exists() else None
self.updated = self._last_post.created
self.last_post_on = self._last_post.created
self._simple_save()
# Trigger the forum-level trackers update
self.forum.update_trackers()
Expand Down
5 changes: 3 additions & 2 deletions machina/apps/forum_conversation/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,15 @@ class Migration(migrations.Migration):
('approved', models.BooleanField(default=True, verbose_name='Approved')),
('posts_count', models.PositiveIntegerField(default=0, verbose_name='Posts count', editable=False, blank=True)),
('views_count', models.PositiveIntegerField(default=0, verbose_name='Views count', editable=False, blank=True)),
('last_post_on', models.DateTimeField(null=True, verbose_name='Last post added on', blank=True)),
('forum', models.ForeignKey(related_name='topics', verbose_name='Topic forum', to='forum.Forum')),
('poster', models.ForeignKey(verbose_name='Poster', to=settings.AUTH_USER_MODEL)),
('subscribers', models.ManyToManyField(related_name='subscriptions', null=True, verbose_name='Subscribers', to=settings.AUTH_USER_MODEL, blank=True)),
],
options={
'ordering': ['-type', '-updated'],
'ordering': ['-type', '-last_post_on'],
'abstract': False,
'get_latest_by': 'updated',
'get_latest_by': 'last_post_on',
'verbose_name': 'Topic',
'verbose_name_plural': 'Topics',
},
Expand Down
2 changes: 1 addition & 1 deletion machina/apps/forum_feeds/feeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def get_object(self, request, *args, **kwargs):
Forum.objects.all(), request.user)

def items(self):
return Topic.objects.filter(forum__in=self.forums, approved=True).order_by('-updated')
return Topic.objects.filter(forum__in=self.forums, approved=True).order_by('-last_post_on')

def item_pubdate(self, item):
return item.created
4 changes: 2 additions & 2 deletions machina/apps/forum_tracking/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def get_unread_topics(self, topics, user):
if topic_tracks.exists():
tracks_dict = dict(topic_tracks.values_list('topic__pk', 'mark_time'))
for topic in topics:
topic_last_modification_date = topic.updated or topic.created
topic_last_modification_date = topic.last_post_on or topic.created
if topic.id in tracks_dict.keys() and topic_last_modification_date > tracks_dict[topic.id]:
unread_topics.append(topic)

Expand All @@ -81,7 +81,7 @@ def get_unread_topics(self, topics, user):
if forum_tracks.exists():
tracks_dict = dict(forum_tracks.values_list('forum__pk', 'mark_time'))
for topic in topics:
topic_last_modification_date = topic.updated or topic.created
topic_last_modification_date = topic.last_post_on or topic.created
if ((topic.forum.id in tracks_dict.keys() and topic.id not in tracked_topics) and
topic_last_modification_date > tracks_dict[topic.forum.id]):
unread_topics.append(topic)
Expand Down
3 changes: 2 additions & 1 deletion machina/apps/forum_tracking/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ def get_unread_forums_from_list(self, forums, user):
tracked_forums = []

for track in tracks:
if track.mark_time < track.forum.updated and track.forum not in unread_forums:
if (track.forum.last_post_on and track.mark_time < track.forum.last_post_on) \
and track.forum not in unread_forums:
unread_forums.extend(track.forum.get_ancestors(include_self=True))
tracked_forums.append(track.forum)

Expand Down
6 changes: 3 additions & 3 deletions machina/apps/forum_tracking/receivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,16 @@ def update_user_trackers(sender, topic, user, request, response, **kwargs):
except ForumReadTrack.DoesNotExist:
forum_track = None

if forum_track is None or forum_track.mark_time < topic.updated:
if forum_track is None or (topic.last_post_on and forum_track.mark_time < topic.last_post_on):
topic_track, created = TopicReadTrack.objects.get_or_create(topic=topic, user=user)
if not created:
topic_track.save() # mark_time filled

# If no other topic is unread inside the considered forum, the latter should also
# be marked as read.
unread_topics = forum.topics.filter(
Q(tracks__user=user, tracks__mark_time__lt=F('updated')) |
Q(forum__tracks__user=user, forum__tracks__mark_time__lt=F('updated'), tracks__isnull=True)).exclude(id=topic.id)
Q(tracks__user=user, tracks__mark_time__lt=F('last_post_on')) |
Q(forum__tracks__user=user, forum__tracks__mark_time__lt=F('last_post_on'), tracks__isnull=True)).exclude(id=topic.id)

if not unread_topics.exists():
# The topics that are marked as read inside the forum for the given user
Expand Down
2 changes: 1 addition & 1 deletion machina/apps/forum_tracking/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def get_queryset(self):
Forum.objects.all(), self.request.user)
topics = Topic.objects.filter(forum__in=forums)
topics_pk = map(lambda t: t.pk, track_handler.get_unread_topics(topics, self.request.user))
return Topic.objects.filter(pk__in=topics_pk).order_by('-updated')
return Topic.objects.filter(pk__in=topics_pk).order_by('-last_post_on')

@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,15 @@ class Migration(migrations.Migration):
('approved', models.BooleanField(default=True, verbose_name='Approved')),
('posts_count', models.PositiveIntegerField(default=0, verbose_name='Posts count', editable=False, blank=True)),
('views_count', models.PositiveIntegerField(default=0, verbose_name='Views count', editable=False, blank=True)),
('last_post_on', models.DateTimeField(null=True, verbose_name='Last post added on', blank=True)),
('forum', models.ForeignKey(related_name='topics', verbose_name='Topic forum', to='forum.Forum')),
('poster', models.ForeignKey(verbose_name='Poster', to=settings.AUTH_USER_MODEL)),
('subscribers', models.ManyToManyField(related_name='subscriptions', null=True, verbose_name='Subscribers', to=settings.AUTH_USER_MODEL, blank=True)),
],
options={
'ordering': ['-type', '-updated'],
'ordering': ['-type', '-last_post_on'],
'abstract': False,
'get_latest_by': 'updated',
'get_latest_by': 'last_post_on',
'verbose_name': 'Topic',
'verbose_name_plural': 'Topics',
},
Expand Down
10 changes: 0 additions & 10 deletions tests/unit/conversation/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,16 +183,6 @@ def test_deletion_should_result_in_the_topic_deletion_if_it_is_alone_in_the_topi
with self.assertRaises(Topic.DoesNotExist):
Topic.objects.get(pk=self.topic_pk)

def test_update_should_not_result_in_the_update_of_the_updated_date_of_the_topic(self):
# Setup
topic_updated_date = self.topic.updated
self.post.content = faker.text()
# Run
self.post.save()
# Check
topic = refresh(self.topic)
self.assertEqual(topic.updated, topic_updated_date)

def test_save_triggers_the_update_of_the_member_posts_count_if_the_related_post_is_approved(self):
# Setup
post = PostFactory.build(topic=self.topic, poster=self.u1)
Expand Down
12 changes: 10 additions & 2 deletions tests/unit/tracking/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,15 @@ def test_says_that_a_topic_with_a_creation_date_greater_than_the_forum_mark_time
# Check
self.assertEqual(unread_topics, [new_topic, ])

def test_says_that_a_topic_with_an_update_date_greater_than_the_forum_mark_time_is_unread(self):
def test_says_that_a_topic_with_a_last_post_date_greater_than_the_forum_mark_time_is_unread(self):
# Setup
PostFactory.create(topic=self.topic, poster=self.u1)
# Run
unread_topics = self.tracks_handler.get_unread_topics(self.forum_2.topics.all(), self.u2)
# Check
self.assertEqual(unread_topics, [self.topic, ])

def test_says_that_a_topic_with_an_update_date_greater_than_its_mark_time_is_unread(self):
def test_says_that_a_topic_with_a_last_post_date_greater_than_its_mark_time_is_unread(self):
# Setup
TopicReadTrackFactory.create(topic=self.topic, user=self.u2)
PostFactory.create(topic=self.topic, poster=self.u1)
Expand Down Expand Up @@ -155,3 +155,11 @@ def test_cannot_say_that_a_topic_is_unread_if_it_has_been_edited(self):
# Check
self.assertFalse(len(unread_forums))
self.assertFalse(len(unread_topics))

def test_cannot_say_that_a_forum_is_unread_if_it_has_been_updated_without_new_topics_or_posts(self):
# Setup
self.forum_2.save()
# Run
unread_forums = self.tracks_handler.get_unread_forums(Forum.objects.all(), self.u2)
# Check
self.assertFalse(len(unread_forums))

0 comments on commit 088cc76

Please sign in to comment.