post.py
294 lines
| 8.7 KiB
| text/x-python
|
PythonLexer
neko259
|
r384 | import os | |
from random import random | |||
import time | |||
import math | |||
neko259
|
r386 | import re | |
neko259
|
r384 | ||
from django.db import models | |||
from django.http import Http404 | |||
from django.utils import timezone | |||
from markupfield.fields import MarkupField | |||
from neboard import settings | |||
neko259
|
r394 | from boards import settings as boards_settings | |
neko259
|
r384 | from boards import thumbs | |
BAN_REASON_AUTO = 'Auto' | |||
IMAGE_THUMB_SIZE = (200, 150) | |||
TITLE_MAX_LENGTH = 50 | |||
DEFAULT_MARKUP_TYPE = 'markdown' | |||
NO_PARENT = -1 | |||
NO_IP = '0.0.0.0' | |||
UNKNOWN_UA = '' | |||
ALL_PAGES = -1 | |||
IMAGES_DIRECTORY = 'images/' | |||
FILE_EXTENSION_DELIMITER = '.' | |||
SETTING_MODERATE = "moderate" | |||
REGEX_REPLY = re.compile('>>(\d+)') | |||
class PostManager(models.Manager): | |||
def create_post(self, title, text, image=None, thread=None, | |||
ip=NO_IP, tags=None, user=None): | |||
posting_time = timezone.now() | |||
neko259
|
r398 | if not thread: | |
thread = Thread.objects.create(bump_time=posting_time, | |||
last_edit_time=posting_time) | |||
else: | |||
thread.bump() | |||
thread.last_edit_time = posting_time | |||
thread.save() | |||
neko259
|
r384 | ||
post = self.create(title=title, | |||
text=text, | |||
pub_time=posting_time, | |||
neko259
|
r398 | thread_new=thread, | |
neko259
|
r384 | image=image, | |
poster_ip=ip, | |||
poster_user_agent=UNKNOWN_UA, | |||
last_edit_time=posting_time, | |||
user=user) | |||
neko259
|
r398 | thread.replies.add(post) | |
neko259
|
r384 | if tags: | |
linked_tags = [] | |||
for tag in tags: | |||
tag_linked_tags = tag.get_linked_tags() | |||
if len(tag_linked_tags) > 0: | |||
linked_tags.extend(tag_linked_tags) | |||
tags.extend(linked_tags) | |||
neko259
|
r398 | map(thread.add_tag, tags) | |
neko259
|
r384 | ||
neko259
|
r398 | self._delete_old_threads() | |
neko259
|
r384 | self.connect_replies(post) | |
return post | |||
def delete_post(self, post): | |||
neko259
|
r398 | thread = post.thread_new | |
thread.last_edit_time = timezone.now() | |||
thread.save() | |||
neko259
|
r384 | ||
post.delete() | |||
def delete_posts_by_ip(self, ip): | |||
posts = self.filter(poster_ip=ip) | |||
map(self.delete_post, posts) | |||
neko259
|
r400 | # TODO Move this method to thread manager | |
neko259
|
r384 | def get_threads(self, tag=None, page=ALL_PAGES, | |
order_by='-bump_time'): | |||
if tag: | |||
threads = tag.threads | |||
neko259
|
r398 | if not threads.exists(): | |
neko259
|
r384 | raise Http404 | |
else: | |||
neko259
|
r398 | threads = Thread.objects.all() | |
neko259
|
r384 | ||
threads = threads.order_by(order_by) | |||
if page != ALL_PAGES: | |||
thread_count = threads.count() | |||
if page < self._get_page_count(thread_count): | |||
start_thread = page * settings.THREADS_PER_PAGE | |||
end_thread = min(start_thread + settings.THREADS_PER_PAGE, | |||
thread_count) | |||
threads = threads[start_thread:end_thread] | |||
return threads | |||
neko259
|
r398 | # TODO Move this method to thread manager | |
neko259
|
r384 | def get_thread_page_count(self, tag=None): | |
if tag: | |||
neko259
|
r398 | threads = Thread.objects.filter(tags=tag) | |
neko259
|
r384 | else: | |
neko259
|
r398 | threads = Thread.objects.all() | |
neko259
|
r384 | ||
return self._get_page_count(threads.count()) | |||
neko259
|
r398 | # TODO Move this method to thread manager | |
neko259
|
r384 | def _delete_old_threads(self): | |
""" | |||
Preserves maximum thread count. If there are too many threads, | |||
delete the old ones. | |||
""" | |||
# TODO Move old threads to the archive instead of deleting them. | |||
# Maybe make some 'old' field in the model to indicate the thread | |||
# must not be shown and be able for replying. | |||
neko259
|
r398 | threads = Thread.objects.all() | |
neko259
|
r384 | thread_count = threads.count() | |
if thread_count > settings.MAX_THREAD_COUNT: | |||
num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT | |||
old_threads = threads[thread_count - num_threads_to_delete:] | |||
neko259
|
r398 | map(Thread.delete_with_posts, old_threads) | |
neko259
|
r384 | ||
def connect_replies(self, post): | |||
"""Connect replies to a post to show them as a refmap""" | |||
for reply_number in re.finditer(REGEX_REPLY, post.text.raw): | |||
post_id = reply_number.group(1) | |||
ref_post = self.filter(id=post_id) | |||
if ref_post.count() > 0: | |||
referenced_post = ref_post[0] | |||
referenced_post.referenced_posts.add(post) | |||
referenced_post.last_edit_time = post.pub_time | |||
referenced_post.save() | |||
def _get_page_count(self, thread_count): | |||
return int(math.ceil(thread_count / float(settings.THREADS_PER_PAGE))) | |||
class Post(models.Model): | |||
"""A post is a message.""" | |||
objects = PostManager() | |||
class Meta: | |||
app_label = 'boards' | |||
def _update_image_filename(self, filename): | |||
"""Get unique image filename""" | |||
path = IMAGES_DIRECTORY | |||
new_name = str(int(time.mktime(time.gmtime()))) | |||
new_name += str(int(random() * 1000)) | |||
new_name += FILE_EXTENSION_DELIMITER | |||
new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0] | |||
return os.path.join(path, new_name) | |||
title = models.CharField(max_length=TITLE_MAX_LENGTH) | |||
pub_time = models.DateTimeField() | |||
text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE, | |||
escape_html=False) | |||
image_width = models.IntegerField(default=0) | |||
image_height = models.IntegerField(default=0) | |||
image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename, | |||
blank=True, sizes=(IMAGE_THUMB_SIZE,), | |||
width_field='image_width', | |||
height_field='image_height') | |||
poster_ip = models.GenericIPAddressField() | |||
poster_user_agent = models.TextField() | |||
thread = models.ForeignKey('Post', null=True, default=None) | |||
neko259
|
r398 | thread_new = models.ForeignKey('Thread', null=True, default=None) | |
neko259
|
r384 | last_edit_time = models.DateTimeField() | |
user = models.ForeignKey('User', null=True, default=None) | |||
referenced_posts = models.ManyToManyField('Post', symmetrical=False, | |||
null=True, | |||
blank=True, related_name='rfp+') | |||
def __unicode__(self): | |||
return '#' + str(self.id) + ' ' + self.title + ' (' + \ | |||
self.text.raw[:50] + ')' | |||
def get_title(self): | |||
title = self.title | |||
if len(title) == 0: | |||
title = self.text.raw[:20] | |||
return title | |||
neko259
|
r398 | def get_sorted_referenced_posts(self): | |
return self.referenced_posts.order_by('id') | |||
def is_referenced(self): | |||
return self.referenced_posts.all().exists() | |||
neko259
|
r400 | def is_opening(self): | |
return self.thread_new.get_replies()[0] == self | |||
neko259
|
r398 | ||
class Thread(models.Model): | |||
class Meta: | |||
app_label = 'boards' | |||
tags = models.ManyToManyField('Tag') | |||
bump_time = models.DateTimeField() | |||
last_edit_time = models.DateTimeField() | |||
replies = models.ManyToManyField('Post', symmetrical=False, null=True, | |||
blank=True, related_name='tre+') | |||
def get_tags(self): | |||
"""Get a sorted tag list""" | |||
return self.tags.order_by('name') | |||
def bump(self): | |||
"""Bump (move to up) thread""" | |||
if self.can_bump(): | |||
self.bump_time = timezone.now() | |||
neko259
|
r384 | def get_reply_count(self): | |
return self.replies.count() | |||
def get_images_count(self): | |||
neko259
|
r398 | return self.replies.filter(image_width__gt=0).count() | |
neko259
|
r384 | ||
def can_bump(self): | |||
"""Check if the thread can be bumped by replying""" | |||
post_count = self.get_reply_count() | |||
return post_count <= settings.MAX_POSTS_PER_THREAD | |||
neko259
|
r398 | def delete_with_posts(self): | |
"""Completely delete thread""" | |||
neko259
|
r384 | ||
neko259
|
r398 | if self.replies.count() > 0: | |
map(Post.objects.delete_post, self.replies.all()) | |||
self.delete() | |||
neko259
|
r384 | ||
def get_last_replies(self): | |||
neko259
|
r398 | """Get last replies, not including opening post""" | |
neko259
|
r384 | if settings.LAST_REPLIES_COUNT > 0: | |
reply_count = self.get_reply_count() | |||
if reply_count > 0: | |||
reply_count_to_show = min(settings.LAST_REPLIES_COUNT, | |||
neko259
|
r398 | reply_count - 1) | |
neko259
|
r384 | last_replies = self.replies.all().order_by('pub_time')[ | |
reply_count - reply_count_to_show:] | |||
return last_replies | |||
neko259
|
r398 | def get_replies(self): | |
"""Get sorted thread posts""" | |||
neko259
|
r384 | ||
neko259
|
r398 | return self.replies.all().order_by('pub_time') | |
neko259
|
r384 | ||
neko259
|
r398 | def add_tag(self, tag): | |
"""Connect thread to a tag and tag to a thread""" | |||
neko259
|
r384 | ||
neko259
|
r398 | self.tags.add(tag) | |
tag.threads.add(self) | |||
neko259
|
r400 | def get_opening_post(self): | |
return self.get_replies()[0] | |||
neko259
|
r398 | def __unicode__(self): | |
neko259
|
r402 | return str(self.get_replies()[0].id) | |
def get_pub_time(self): | |||
return self.get_opening_post().pub_time |