def get_posts(user, topic="", tag="", order="rank", limit=None): """ Generates a post list on a topic. """ # Topics are case insensitive. topic = topic.lower() # Detect known post types. post_type = POST_TYPE_MAPPER.get(topic) # Determines how to start the query. if post_type: query = Post.objects.filter(type=post_type) elif topic == OPEN: query = Post.objects.filter(type=Post.QUESTION, reply_count=0) elif topic == BOOKMARKS and user.is_authenticated: query = Post.objects.filter(votes__author=user, votes__type=Vote.BOOKMARK) elif topic == FOLLOWING and user.is_authenticated: query = Post.objects.exclude( subs__type=Subscription.NO_MESSAGES).filter(subs__user=user) elif topic == MYPOSTS and user.is_authenticated: query = Post.objects.filter(author=user) elif topic == MYVOTES: #TODO: switching to votes #votes_query = Vote.objects.filter(post__author=user).exclude(author=user) #query = votes_query.values("post") query = Post.objects.filter(votes__post__author=user).exclude( votes__author=user) else: query = Post.objects.filter(type__in=Post.TOP_LEVEL) # Filter by tags if specified. if tag: query = query.filter(tag_val__iregex=tag) # Apply post ordering. if ORDER_MAPPER.get(order): ordering = ORDER_MAPPER.get(order) query = query.order_by(ordering) else: query = query.order_by("-rank") days = LIMIT_MAP.get(limit, 0) # Apply time limit if required. if days: delta = util.now() - timedelta(days=days) query = query.filter(lastedit_date__gt=delta) # Select related information used during rendering. query = query.prefetch_related("root", "author__profile", "lastedit_user__profile", "thread_users__profile") return query
def bump(request, post, **kwargs): now = util.now() user = request.user Post.objects.filter(uid=post.uid).update(lastedit_date=now, rank=now.timestamp()) msg = f"bumped post" url = post.get_absolute_url() messages.info(request, mark_safe(msg)) auth.db_logger(user=user, text=f"{msg}", post=post) return url
def fake(): """ Create a fake blog post. """ # Get or create a blog blog, created = Blog.objects.get_or_create(title="Fake") BlogPost.objects.create(blog=blog, title='Creating a fake blog post.', creation_date=now())
def get_posts(user, topic="", tag="", order="", limit=None): """ Generates a post list on a topic. """ # Topics are case insensitive. topic = topic or LATEST topic = topic.lower() # Detect known post types. post_type = POST_TYPE_MAPPER.get(topic) query = Post.objects.valid_posts(u=user, is_toplevel=True) # Determines how to start the preform_search. if post_type: query = query.filter(type=post_type) elif topic == OPEN: query = query.filter(type=Post.QUESTION, answer_count=0) elif topic == BOOKMARKS and user.is_authenticated: query = query.filter(votes__author=user, votes__type=Vote.BOOKMARK) elif topic == FOLLOWING and user.is_authenticated: query = query.filter(subs__user=user) elif topic == MYPOSTS and user.is_authenticated: query = query.filter(author=user) elif topic == MYVOTES and user.is_authenticated: query = query.filter(votes__post__author=user) elif topic == MYTAGS and user.is_authenticated: tags = user.profile.my_tags.split(",") query = query.filter(tags__name__in=tags) if user.is_anonymous or not user.profile.is_moderator: query = query.exclude(Q(spam=Post.SPAM) | Q(status=Post.DELETED)) # Filter by tags if specified. if tag: query = query.filter(tags__name=tag.lower()) # Apply post ordering. if ORDER_MAPPER.get(order): ordering = ORDER_MAPPER.get(order) query = query.order_by(ordering) else: query = query.order_by("-rank") days = LIMIT_MAP.get(limit, 0) # Apply time limit if required. if days: delta = util.now() - timedelta(days=days) query = query.filter(lastedit_date__gt=delta) # Select related information used during rendering. query = query.select_related("root").select_related( "author__profile", "lastedit_user__profile") return query
def init_local(update): """ Creates a local blog """ blog, created = Blog.objects.get_or_create(title="Local blog", remote=False) for step in range(update): logger.info("adding local blog post") BlogPost.objects.create(blog=blog, title='Local blog post', content="Lorem ipsum", creation_date=now())
def get_start(days): """ Return the start date stored in cache. """ if days < 0: recent = Sync.objects.filter(pk=1).first() recent = recent.last_synced if recent else None else: recent = Post.objects.old().order_by('-creation_date').first() recent = recent.creation_date if recent else None start = recent or util.now() return start
def get_start(days, cursor): """ Return the start date stored in database """ if days < 0: recent = Sync.objects.filter(pk=1).first() recent = recent.last_synced if recent else most_recent(cursor=cursor) recent = recent + timedelta(days=2) else: # Get the most recently created post on the remote server. recent = Post.objects.old().order_by('-creation_date').first() recent = recent.creation_date if recent else None recent = recent - timedelta(days=2) start = recent or util.now() return start
def create_user_awards(user_id): from biostar.accounts.models import User from biostar.forum.models import Award, Badge, Post from biostar.forum.awards import ALL_AWARDS from biostar.forum import util user = User.objects.filter(id=user_id).first() # debugging # Award.objects.all().delete() # Collect valid targets valid = [] for award in ALL_AWARDS: # Valid award targets the user has earned targets = award.validate(user) for target in targets: date = util.now() post = target if isinstance(target, Post) else None badge = Badge.objects.filter(name=award.name).first() # Do not award a post multiple times. already_awarded = Award.objects.filter(user=user, badge=badge, post=post).exists() if post and already_awarded: continue valid.append((user, badge, date, post)) # Pick random awards to give to user random.shuffle(valid) valid = valid[:settings.MAX_AWARDS] for target in valid: user, badge, date, post = target # Create an award for each target. Award.objects.create(user=user, badge=badge, date=date, post=post) message(f"award {badge.name} created for {user.email}")
def apply_sort(posts, limit=None, order=None): # Apply post ordering. if ORDER_MAPPER.get(order): ordering = ORDER_MAPPER.get(order) posts = posts.order_by(ordering) else: posts = posts.order_by('-rank') days = LIMIT_MAP.get(limit, 0) # Apply time limit if required. if days: delta = util.now() - timedelta(days=days) posts = posts.filter(lastedit_date__gt=delta) # Select related information used during rendering. posts = posts.select_related("root").select_related( "author__profile", "lastedit_user__profile") return posts
def edit(self): """ Edit an existing post. """ if self.user != self.post.author and not self.user.profile.is_moderator: raise forms.ValidationError("Only the author or a moderator can edit a post.") data = self.cleaned_data self.post.title = data.get('title') content = data.get('content', self.post.content) log_edits(user=self.user, post=self.post) self.post.content = content self.post.type = data.get('post_type') self.post.tag_val = data.get('tag_val') self.post.lastedit_date = util.now() self.post.lastedit_user = self.user self.post.save() return self.post
def community_list(request): users = User.objects.select_related("profile") page = request.GET.get("page", 1) ordering = request.GET.get("order", "visit") limit_to = request.GET.get("limit", "time") query = request.GET.get('query', '') query = query.replace("'", "").replace('"', '').strip() days = LIMIT_MAP.get(limit_to, 0) if days: delta = util.now() - timedelta(days=days) users = users.filter(profile__last_login__gt=delta) if query and len(query) > 2: db_query = Q(profile__name__icontains=query) | Q(profile__uid__icontains=query) | \ Q(username__icontains=query) | Q(email__icontains=query) users = users.filter(db_query) # Remove the cache when filters are given. no_cache = days or (query and len(query) > 2) or ordering cache_key = None if no_cache else USERS_LIST_KEY order = ORDER_MAPPER.get(ordering, "visit") users = users.filter(profile__state__in=[Profile.NEW, Profile.TRUSTED]) users = users.order_by(order) # Create the paginator paginator = CachedPaginator(cache_key=cache_key, object_list=users, per_page=settings.POSTS_PER_PAGE) users = paginator.get_page(page) context = dict(tab="community", users=users, query=query, order=ordering, limit=limit_to) return render(request, "community_list.html", context=context)
def community_list(request): users = User.objects.select_related("profile") page = request.GET.get("page", 1) ordering = request.GET.get("order", "visit") limit_to = request.GET.get("limit", "time") days = LIMIT_MAP.get(limit_to, 0) if days: delta = util.now() - timedelta(days=days) users = users.filter(profile__last_login__gt=delta) order = ORDER_MAPPER.get(ordering, "visit") users = users.order_by(order) paginator = Paginator(users, settings.USERS_PER_PAGE) users = paginator.get_page(page) context = dict(community="active", objs=users, order=ordering, limit=limit_to) return render(request, "community_list.html", context=context)
def herald_publisher(request, limit=20, nmin=1): """ Create one publication from Herald accepted submissions ( up to 'limit' ). """ # Reset status on published links. # SharedLink.objects.filter(status=SharedLink.PUBLISHED).update(status=SharedLink.ACCEPTED) heralds = SharedLink.objects.filter( status=SharedLink.ACCEPTED).order_by('-pk')[:limit] count = heralds.count() if count < nmin: logger.warning( f"Not enough stories to publish, minimum of {nmin} required.") return # Create herald content date = util.now() date_fmt = date.strftime("%A, %B %d, %Y") title = f"The Biostar Herald for {date_fmt}" port = f':{settings.HTTP_PORT}' if settings.HTTP_PORT else '' base_url = f"{settings.PROTOCOL}://{settings.SITE_DOMAIN}{port}" authors = set(h.author for h in heralds) editors = set(h.editor for h in heralds) subscribe_url = reverse('herald_subscribe') context = dict(heralds=heralds, title=title, site_domain=settings.SITE_DOMAIN, protocol=settings.PROTOCOL, base_url=base_url, authors=authors, editors=editors, subscribe_url=subscribe_url) content = render_template(template="herald/herald_content.md", context=context) # Create herald post user = User.objects.filter(is_superuser=True).first() post = auth.create_post(title=title, content=content, author=user, tag_val='herald', ptype=Post.HERALD, nodups=False, request=request) # Tie these submissions to herald post hpks = heralds.values_list('pk', flat=True) SharedLink.objects.filter(pk__in=hpks).update(status=SharedLink.PUBLISHED, post=post, lastedit_date=date) # Log the action auth.db_logger(user=user, text=f"published {count} submissions in {title}") # Create a herald blog post herald_blog(post=post) # Send out herald emails herald_emails.spool(uid=post.uid) # Clean up declined links remove_declined() return post
def finalize_post(sender, instance, created, **kwargs): # Determine the root of the post. root = instance.root if instance.root is not None else instance # Update last contributor, last editor, and last edit date to the thread Post.objects.filter(uid=root.uid).update( lastedit_user=instance.lastedit_user, last_contributor=instance.last_contributor, lastedit_date=instance.lastedit_date) # Get newly created subscriptions since the last edit date. subs = Subscription.objects.filter(date__gte=instance.lastedit_date, post=instance.root) if created: # Make the Uid user friendly instance.uid = instance.uid or f"p{instance.pk}" if instance.parent: # When the parent is set the root must follow the parent root. instance.root = instance.parent.root else: # When there is no parent, root and parent are set to itself. instance.root = instance.parent = instance # Answers and comments may only have comments associated with them. if instance.parent.type in (Post.ANSWER, Post.COMMENT): instance.type = Post.COMMENT # Sanity check. assert instance.root and instance.parent if instance.is_toplevel: # Add tags for top level posts. tags = [ Tag.objects.get_or_create(name=name)[0] for name in instance.parse_tags() ] instance.tags.remove() instance.tags.add(*tags) else: # Title is inherited from top level. instance.title = "%s: %s" % (instance.get_type_display(), instance.root.title[:80]) # Make the last editor first in the list of contributors # Done on post creation to avoid moderators being added for editing a post. instance.root.thread_users.remove(instance.lastedit_user) instance.root.thread_users.add(instance.lastedit_user) # Update this post rank on create and not every edit. instance.rank = instance.lastedit_date.timestamp() # Save the instance. instance.save() instance.update_parent_counts() # Bump the root rank when a new descendant is added. Post.objects.filter(uid=instance.root.uid).update( rank=util.now().timestamp()) # Create subscription to the root. auth.create_subscription(post=instance.root, user=instance.author) # Get all subscribed users when a new post is created subs = Subscription.objects.filter(post=instance.root) # Notify users who are watching tags in this post #tasks.notify_watched_tags(post=instance) # Give it a spam score. tasks.spam_scoring.spool(post=instance) # Add this post to the spam index if it's spam. tasks.update_spam_index.spool(post=instance) # Ensure posts get re-indexed after being edited. Post.objects.filter(uid=instance.uid).update(indexed=False) # Exclude current authors from receiving messages from themselves subs = subs.exclude( Q(type=Subscription.NO_MESSAGES) | Q(user=instance.author)) extra_context = dict(post=instance) tasks.notify_followers.spool(subs=subs, author=instance.author, extra_context=extra_context)
def save(self, *args, **kwargs): self.sent_date = self.sent_date or util.now() self.uid = self.uid or util.get_uuid(limit=16) super(Message, self).save(**kwargs)
def past_date(days=2, minutes=0, seconds=0): return util.now() - timedelta(days=days, minutes=minutes, seconds=seconds)
def get_posts(user, topic="", tag="", order="", limit=None): """ Generates a post list on a topic. """ # Topics are case insensitive. topic = topic or LATEST topic = topic.lower() # Detect known post types. post_type = POST_TYPE_MAPPER.get(topic) query = Post.objects.valid_posts(u=user, is_toplevel=True) # Determines how to start the preform_search. if post_type: query = query.filter(type=post_type) elif topic == SHOW_SPAM: query = query.filter(Q(spam=Post.SPAM) | Q(spam=Post.SUSPECT)) elif topic == OPEN: query = query.filter(type=Post.QUESTION, answer_count=0) elif topic == BOOKMARKS and user.is_authenticated: query = query.filter(votes__author=user, votes__type=Vote.BOOKMARK) elif topic == FOLLOWING and user.is_authenticated: query = query.filter(subs__user=user).exclude( subs__type=Subscription.NO_MESSAGES) elif topic == MYPOSTS and user.is_authenticated: # Show users all of there posts ( deleted, spam, or quarantined ) query = Post.objects.filter(author=user) #query = query.filter(author=user) elif topic == MYVOTES and user.is_authenticated: query = query.filter(votes__post__author=user) elif topic == MYTAGS and user.is_authenticated: tags = map(lambda t: t.lower(), user.profile.my_tags.split(",")) query = query.filter(tags__name__in=tags).distinct() else: # Exclude spam posts unless specifically on the tab. query = query.exclude(Q(spam=Post.SPAM)) # Filter by tags if specified. if tag: query = query.filter(tags__name=tag.lower()) # Apply post ordering. if ORDER_MAPPER.get(order): ordering = ORDER_MAPPER.get(order) query = query.order_by(ordering) else: query = query.order_by("-rank") days = LIMIT_MAP.get(limit, 0) # Apply time limit if required. if days: delta = util.now() - timedelta(days=days) query = query.filter(lastedit_date__gt=delta) # Select related information used during rendering. query = query.select_related("root").select_related( "author__profile", "lastedit_user__profile") return query
def get_posts(request, topic="", order="", limit=None): """ Generates a post list on a topic. """ user = request.user # Topics are case insensitive. topic = topic or LATEST topic = topic.lower() # Detect known post types. post_type = POST_TYPE.get(topic) # Get all open top level posts. posts = Post.objects.filter(is_toplevel=True, root__status=Post.OPEN) # Filter for various post types. if post_type: posts = posts.filter(type=post_type) elif topic == SHOW_SPAM and user.profile.is_moderator: posts = get_spam(request) elif topic == OPEN: posts = posts.filter(type=Post.QUESTION, answer_count=0) elif topic == BOOKMARKS and user.is_authenticated: posts = Post.objects.filter(votes__author=user, votes__type=Vote.BOOKMARK) elif topic == FOLLOWING and user.is_authenticated: posts = posts.filter(subs__user=user).exclude( subs__type=Subscription.NO_MESSAGES) elif topic == MYPOSTS and user.is_authenticated: # Show users all of their posts ( deleted, spam, or quarantined ) posts = Post.objects.filter(author=user) elif topic == MYVOTES and user.is_authenticated: posts = posts.filter(votes__post__author=user) elif topic == MYTAGS and user.is_authenticated: tags = map(lambda t: t.lower(), user.profile.my_tags.split(",")) posts = posts.filter(tags__name__in=tags).distinct() # Search for tags elif topic != LATEST and (topic not in POST_TYPE): posts = posts.filter(tags__name=topic.lower()) messages.success(request, f"Filtering for tag: {topic}") # Apply post ordering. if ORDER_MAPPER.get(order): ordering = ORDER_MAPPER.get(order) posts = posts.order_by(ordering) else: posts = posts.order_by("-rank") days = LIMIT_MAP.get(limit, 0) # Apply time limit if required. if days: delta = util.now() - timedelta(days=days) posts = posts.filter(lastedit_date__gt=delta) # Select related information used during rendering. posts = posts.select_related("root").select_related( "author__profile", "lastedit_user__profile") return posts
def time_ago(days=2, minutes=0, seconds=0): return util.now() - timedelta(days=days, minutes=minutes, seconds=seconds)
def items(self): # Delay posts hours. delay_time = now() - timedelta(hours=2) posts = Post.objects.valid_posts(creation_date__lt=delay_time).exclude( type=Post.BLOG).order_by('-creation_date') return posts[:FEED_COUNT]
def finalize_post(sender, instance, created, **kwargs): # Determine the root of the post. root = instance.root if instance.root is not None else instance # Update last contributor, last editor, and last edit date to the thread Post.objects.filter(uid=root.uid).update( lastedit_user=instance.lastedit_user, lastedit_date=instance.lastedit_date) # Get newly created subscriptions since the last edit date. subs = Subscription.objects.filter(date__gte=instance.lastedit_date, post=instance.root) extra_context = dict() if created: # Make the uid user friendly instance.uid = instance.uid or f"9{instance.pk}" if instance.parent: # When the parent is set the root must follow the parent root. instance.root = instance.parent.root else: # When there is no parent, root and parent are set to itself. instance.root = instance.parent = instance # Answers and comments may only have comments associated with them. if instance.parent.type in (Post.ANSWER, Post.COMMENT): instance.type = Post.COMMENT # Sanity check. assert instance.root and instance.parent # Make the last editor first in the list of contributors # Done on post creation to avoid moderators being added for editing a post. instance.root.thread_users.remove(instance.lastedit_user) instance.root.thread_users.add(instance.lastedit_user) # Update this post rank on create and not every edit. instance.rank = instance.lastedit_date.timestamp() # Save the instance. instance.save() instance.update_parent_counts() # Bump the root rank when a new answer is added. if instance.is_answer: Post.objects.filter(uid=instance.root.uid).update( rank=util.now().timestamp()) # Create subscription to the root. auth.create_subscription(post=instance.root, user=instance.author) # Get all subscribed users when a new post is created subs = Subscription.objects.filter(post=instance.root) # Notify users who are watching tags in this post tasks.notify_watched_tags.spool(uid=instance.uid, extra_context=extra_context) # Send out mailing list when post is created. tasks.mailing_list.spool(uid=instance.uid, extra_context=extra_context) # Set the tags on the instance. if instance.is_toplevel: tags = [ Tag.objects.get_or_create(name=name)[0] for name in instance.parse_tags() ] instance.tags.clear() instance.tags.add(*tags) # Ensure spam posts get closed status if instance.is_spam: Post.objects.filter(uid=instance.uid).update(status=Post.CLOSED) if not instance.is_toplevel: # Title is inherited from top level. title = f"{instance.get_type_display()}: {instance.root.title[:80]}" Post.objects.filter(uid=instance.uid).update(title=title) # Ensure posts get re-indexed after being edited. Post.objects.filter(uid=instance.uid).update(indexed=False) # Exclude current authors from receiving messages from themselves subs = subs.exclude( Q(type=Subscription.NO_MESSAGES) | Q(user=instance.author)) sub_ids = list(subs.values_list('id', flat=True)) # Notify subscribers tasks.notify_followers.spool(sub_ids=sub_ids, author_id=instance.author.pk, uid=instance.uid, extra_context=extra_context)