def get_categories_tree(user, parent=None): if not user.acl['visible_categories']: return [] if parent: queryset = parent.get_descendants().order_by('lft') else: queryset = Category.objects.all_categories() queryset_with_acl = queryset.filter(id__in=user.acl['visible_categories']) visible_categories = list(queryset_with_acl) categories_dict = {} categories_list = [] parent_level = parent.level + 1 if parent else 1 for category in visible_categories: category.subcategories = [] categories_dict[category.pk] = category categories_list.append(category) if category.parent_id and category.level > parent_level: categories_dict[category.parent_id].subcategories.append(category) add_acl(user, categories_list) categoriestracker.make_read_aware(user, categories_list) for category in reversed(visible_categories): if category.acl['can_browse']: category.parent = categories_dict.get(category.parent_id) if category.parent: category.parent.threads += category.threads category.parent.posts += category.posts if category.parent.last_post_on and category.last_post_on: parent_last_post = category.parent.last_post_on category_last_post = category.last_post_on update_last_thead = parent_last_post < category_last_post elif not category.parent.last_post_on and category.last_post_on: update_last_thead = True else: update_last_thead = False if update_last_thead: category.parent.last_post_on = category.last_post_on category.parent.last_thread_id = category.last_thread_id category.parent.last_thread_title = category.last_thread_title category.parent.last_thread_slug = category.last_thread_slug category.parent.last_poster_name = category.last_poster_name category.parent.last_poster_slug = category.last_poster_slug if not category.is_read: category.parent.is_read = False flat_list = [] for category in categories_list: if category.level == parent_level: flat_list.append(category) return flat_list
def test_make_read_aware_sets_read_flag_for_empty_category(self): """make_read_aware sets read flag on empty category""" categoriestracker.make_read_aware(self.anon, self.categories) self.assertTrue(self.category.is_read) categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read)
def test_user_unread_thread(self): """tracked thread is marked as unread for authenticated users""" testutils.post_thread(self.category, started_on=timezone.now()) categoriestracker.make_read_aware(self.user, self.category) self.assertFalse(self.category.is_read) self.assertTrue(self.category.is_new)
def test_anon_thread_after_cutoff(self): """tracked thread is marked as read for anonymous users""" testutils.post_thread(self.category, started_on=timezone.now()) categoriestracker.make_read_aware(AnonymousUser(), self.category) self.assertTrue(self.category.is_read) self.assertFalse(self.category.is_new)
def test_thread_read(self): """thread read flag is set for user, then its set as unread by reply""" self.reply_thread() add_acl(self.user, self.categories) threadstracker.make_read_aware(self.user, self.thread) self.assertFalse(self.thread.is_read) threadstracker.read_thread(self.user, self.thread, self.post) threadstracker.make_read_aware(self.user, self.thread) self.assertTrue(self.thread.is_read) categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read) self.thread.last_post_on = timezone.now() self.thread.save() self.category.synchronize() self.category.save() self.reply_thread() threadstracker.make_read_aware(self.user, self.thread) self.assertFalse(self.thread.is_read) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read) posts = [post for post in self.thread.post_set.order_by('id')] threadstracker.make_posts_read_aware(self.user, self.thread, posts) for post in posts[:-1]: self.assertTrue(post.is_read) self.assertFalse(posts[-1].is_read)
def _test_thread_read(self): """thread read flag is set for user, then its set as unread by reply""" self.reply_thread(self.thread) add_acl(self.user, self.categories) threadstracker.make_read_aware(self.user, self.thread) self.assertFalse(self.thread.is_read) threadstracker.read_thread(self.user, self.thread, self.post) threadstracker.make_read_aware(self.user, self.thread) self.assertTrue(self.thread.is_read) categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read) self.thread.last_post_on = timezone.now() self.thread.save() self.category.synchronize() self.category.save() self.reply_thread() threadstracker.make_read_aware(self.user, self.thread) self.assertFalse(self.thread.is_read) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read) posts = [post for post in self.thread.post_set.order_by('id')] threadstracker.make_posts_read_aware(self.user, self.thread, posts) for post in posts[:-1]: self.assertTrue(post.is_read) self.assertFalse(posts[-1].is_read)
def test_sync_record_for_empty_category(self): """sync_record sets read flag on empty category""" add_acl(self.user, self.categories) categoriestracker.sync_record(self.user, self.category) self.user.categoryread_set.get(category=self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read)
def test_user_created_after_thread(self): """tracked thread older than user is marked as read""" started_on = timezone.now() - timedelta(days=1) testutils.post_thread(self.category, started_on=started_on) categoriestracker.make_read_aware(self.user, self.category) self.assertTrue(self.category.is_read) self.assertFalse(self.category.is_new)
def test_user_thread_before_cutoff(self): """non-tracked thread is marked as read for authenticated users""" started_on = timezone.now() - timedelta( days=settings.MISAGO_READTRACKER_CUTOFF) testutils.post_thread(self.category, started_on=started_on) categoriestracker.make_read_aware(self.user, self.category) self.assertTrue(self.category.is_read) self.assertFalse(self.category.is_new)
def test_user_read_post(self): """tracked thread with read post marked as read""" thread = testutils.post_thread(self.category, started_on=timezone.now()) poststracker.save_read(self.user, thread.first_post) categoriestracker.make_read_aware(self.user, self.category) self.assertTrue(self.category.is_read) self.assertFalse(self.category.is_new)
def test_user_first_unread_last_read_post(self): """tracked thread with unread first and last read post marked as unread""" thread = testutils.post_thread(self.category, started_on=timezone.now()) post = testutils.reply_thread(thread, posted_on=timezone.now()) poststracker.save_read(self.user, post) categoriestracker.make_read_aware(self.user, self.category) self.assertFalse(self.category.is_read) self.assertTrue(self.category.is_new)
def test_user_hidden_thread_unread_post(self): """tracked hidden thread""" thread = testutils.post_thread( self.category, started_on=timezone.now(), is_hidden=True, ) categoriestracker.make_read_aware(self.user, self.category) self.assertTrue(self.category.is_read) self.assertFalse(self.category.is_new)
def test_user_first_read_post_unread_event(self): """tracked thread with read first post and unread event""" thread = testutils.post_thread(self.category, started_on=timezone.now()) poststracker.save_read(self.user, thread.first_post) testutils.reply_thread(thread, posted_on=timezone.now(), is_event=True) categoriestracker.make_read_aware(self.user, self.category) self.assertFalse(self.category.is_read) self.assertTrue(self.category.is_new)
def test_user_unapproved_own_thread_unread_post(self): """tracked unapproved but visible thread""" thread = testutils.post_thread( self.category, poster=self.user, started_on=timezone.now(), is_unapproved=True, ) categoriestracker.make_read_aware(self.user, self.category) self.assertFalse(self.category.is_read) self.assertTrue(self.category.is_new)
def test_sync_record_for_category_threads_behind_cutoff(self): """ sync_record sets read flag on category with only thread being behind cutoff """ self.post_thread(timezone.now() - timedelta(days=180)) read_thread = self.post_thread(timezone.now()) threadstracker.make_read_aware(self.user, read_thread) threadstracker.read_thread(self.user, read_thread, read_thread.last_post) category = Category.objects.get(pk=self.category.pk) categoriestracker.make_read_aware(self.user, [category]) self.assertTrue(category.is_read)
def test_user_hidden_event(self): """tracked thread with unread first post and hidden event""" thread = testutils.post_thread(self.category, started_on=timezone.now()) testutils.reply_thread( thread, posted_on=timezone.now(), is_event=True, is_hidden=True, ) categoriestracker.make_read_aware(self.user, self.category) self.assertFalse(self.category.is_read) self.assertTrue(self.category.is_new)
def test_sync_record_for_category_with_deleted_threads(self): """unread category reverts to read after its emptied""" self.post_thread(self.user.reads_cutoff + timedelta(days=1)) self.post_thread(self.user.reads_cutoff + timedelta(days=1)) self.post_thread(self.user.reads_cutoff + timedelta(days=1)) add_acl(self.user, self.categories) categoriestracker.sync_record(self.user, self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read) self.category.thread_set.all().delete() self.category.synchronize() self.category.save() categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read)
def test_sync_record_for_category_deleted_threads(self): """unread category reverts to read after its emptied""" self.post_thread(self.user.joined_on + timedelta(days=1)) self.post_thread(self.user.joined_on + timedelta(days=1)) self.post_thread(self.user.joined_on + timedelta(days=1)) add_acl(self.user, self.categories) categoriestracker.sync_record(self.user, self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read) self.category.thread_set.all().delete() self.category.synchronize() self.category.save() categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read)
def test_sync_record_for_category_with_many_threads(self): """sync_record sets unread flag on category with many threads""" self.post_thread(self.user.reads_cutoff + timedelta(days=1)) self.post_thread(self.user.reads_cutoff - timedelta(days=1)) self.post_thread(self.user.reads_cutoff + timedelta(days=1)) self.post_thread(self.user.reads_cutoff - timedelta(days=1)) add_acl(self.user, self.categories) categoriestracker.sync_record(self.user, self.category) self.user.categoryread_set.get(category=self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read) self.post_thread(self.user.reads_cutoff + timedelta(days=1)) categoriestracker.sync_record(self.user, self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read)
def test_sync_record_for_category_many_threads(self): """sync_record sets unread flag on category with many threads""" self.post_thread(self.user.joined_on + timedelta(days=1)) self.post_thread(self.user.joined_on - timedelta(days=1)) self.post_thread(self.user.joined_on + timedelta(days=1)) self.post_thread(self.user.joined_on - timedelta(days=1)) add_acl(self.user, self.categories) categoriestracker.sync_record(self.user, self.category) self.user.categoryread_set.get(category=self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read) self.post_thread(self.user.joined_on + timedelta(days=1)) categoriestracker.sync_record(self.user, self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read)
def test_sync_record_for_category_new_thread(self): """ sync_record sets read flag on category with old thread, then keeps flag to unread when new reply is posted """ self.post_thread(self.user.joined_on + timedelta(days=1)) add_acl(self.user, self.categories) categoriestracker.sync_record(self.user, self.category) self.user.categoryread_set.get(category=self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read) self.post_thread(self.user.joined_on + timedelta(days=1)) categoriestracker.sync_record(self.user, self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read)
def test_sync_record_for_category_with_new_thread(self): """ sync_record sets read flag on category with old thread, then keeps flag to unread when new reply is posted """ self.post_thread(self.user.reads_cutoff + timedelta(days=1)) add_acl(self.user, self.categories) categoriestracker.sync_record(self.user, self.category) self.user.categoryread_set.get(category=self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read) self.post_thread(self.user.reads_cutoff + timedelta(days=1)) categoriestracker.sync_record(self.user, self.category) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read)
def test_thread_read_category_cutoff(self): """thread read is handled when category cutoff is present""" self.reply_thread() add_acl(self.user, self.categories) threadstracker.make_read_aware(self.user, self.thread) self.assertFalse(self.thread.is_read) categoriestracker.read_category(self.user, self.category) threadstracker.make_read_aware(self.user, self.thread) self.assertTrue(self.thread.is_read) categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read) posts = list(self.thread.post_set.order_by('id')) threadstracker.make_posts_read_aware(self.user, self.thread, posts) for post in posts: self.assertTrue(post.is_read) # post reply self.reply_thread() # test if only last post is unread posts = list(self.thread.post_set.order_by('id')) threadstracker.make_read_aware(self.user, self.thread) threadstracker.make_posts_read_aware(self.user, self.thread, posts) for post in posts[:-1]: self.assertTrue(post.is_read) self.assertTrue(posts[-1].is_new) # last post read will change readstate of categories threadstracker.make_read_aware(self.user, self.thread) threadstracker.read_thread(self.user, self.thread, posts[-1]) categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read)
def test_anon_category_with_recent_reply_read(self): """anon users content is always read""" categoriestracker.make_read_aware(self.anon, self.categories) self.category.last_post_on = timezone.now() self.assertTrue(self.category.is_read)
def test_make_read_aware_sets_unread_flag_for_category_new_thread(self): """make_read_aware sets unread flag on category with new thread""" self.category.last_post_on = self.user.joined_on + timedelta(days=1) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read)
def test_make_read_aware_sets_read_flag_for_category_old_thread(self): """make_read_aware sets read flag on category with old thread""" self.category.last_post_on = self.user.joined_on - timedelta(days=1) categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read)
def test_empty_category_is_read(self): """empty category is read for signed in user""" categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read)
def test_anon_category_recent_reply_read(self): """anon users content is always read""" categoriestracker.make_read_aware(self.anon, self.categories) self.category.last_post_on = timezone.now() self.assertTrue(self.category.is_read)
def test_anon_empty_category_read(self): """anon users content is always read""" categoriestracker.make_read_aware(self.anon, self.categories) self.assertIsNone(self.category.last_post_on) self.assertTrue(self.category.is_read)
def test_falsy_value(self): """passing falsy value to readtracker causes no errors""" categoriestracker.make_read_aware(self.user, None) categoriestracker.make_read_aware(self.user, False) categoriestracker.make_read_aware(self.user, [])
def test_make_read_aware_sets_read_flag_for_category_with_old_thread(self): """make_read_aware sets read flag on category with old thread""" self.category.last_post_on = self.user.reads_cutoff - timedelta(days=1) categoriestracker.make_read_aware(self.user, self.categories) self.assertTrue(self.category.is_read)
def test_make_read_aware_sets_unread_flag_for_category_with_new_thread(self): """make_read_aware sets unread flag on category with new thread""" self.category.last_post_on = self.user.reads_cutoff + timedelta(days=1) categoriestracker.make_read_aware(self.user, self.categories) self.assertFalse(self.category.is_read)