models.py
212 lines
| 5.8 KiB
| text/x-python
|
PythonLexer
/ boards / models.py
neko259
|
r29 | import os | ||
neko259
|
r31 | import re | ||
neko259
|
r0 | from django.db import models | ||
from django.utils import timezone | ||||
neko259
|
r29 | import time | ||
neko259
|
r4 | |||
neko259
|
r22 | from neboard import settings | ||
neko259
|
r39 | from markupfield.fields import MarkupField | ||
neko259
|
r22 | |||
import thumbs | ||||
neko259
|
r4 | NO_PARENT = -1 | ||
NO_IP = '0.0.0.0' | ||||
UNKNOWN_UA = '' | ||||
neko259
|
r22 | |||
neko259
|
r0 | |||
neko259
|
r29 | def update_image_filename(instance, filename): | ||
"""Get unique image filename""" | ||||
path = 'images/' | ||||
new_name = str(int(time.mktime(time.gmtime()))) + '_' + filename | ||||
return os.path.join(path, new_name) | ||||
neko259
|
r2 | class PostManager(models.Manager): | ||
neko259
|
r22 | def create_post(self, title, text, image=None, parent_id=NO_PARENT, | ||
neko259
|
r24 | ip=NO_IP, tags=None): | ||
neko259
|
r22 | post = self.create(title=title, | ||
text=text, | ||||
pub_time=timezone.now(), | ||||
parent=parent_id, | ||||
image=image, | ||||
poster_ip=ip, | ||||
neko259
|
r25 | poster_user_agent=UNKNOWN_UA, | ||
last_edit_time=timezone.now()) | ||||
neko259
|
r0 | |||
neko259
|
r24 | if tags: | ||
for tag in tags: | ||||
post.tags.add(tag) | ||||
neko259
|
r25 | if parent_id != NO_PARENT: | ||
neko259
|
r38 | self._bump_thread(parent_id) | ||
neko259
|
r28 | else: | ||
self._delete_old_threads() | ||||
neko259
|
r25 | |||
neko259
|
r1 | return post | ||
neko259
|
r0 | |||
def delete_post(self, post): | ||||
neko259
|
r22 | children = self.filter(parent=post.id) | ||
neko259
|
r3 | for child in children: | ||
self.delete_post(child) | ||||
neko259
|
r2 | post.delete() | ||
neko259
|
r0 | def delete_posts_by_ip(self, ip): | ||
neko259
|
r22 | posts = self.filter(poster_ip=ip) | ||
neko259
|
r1 | for post in posts: | ||
self.delete_post(post) | ||||
neko259
|
r0 | |||
neko259
|
r22 | def get_threads(self, tag=None): | ||
neko259
|
r27 | if tag: | ||
threads = self.filter(parent=NO_PARENT, tags=tag) | ||||
else: | ||||
neko259
|
r22 | threads = self.filter(parent=NO_PARENT) | ||
neko259
|
r25 | threads = list(threads.order_by('-last_edit_time')) | ||
neko259
|
r5 | |||
return threads | ||||
def get_thread(self, opening_post_id): | ||||
neko259
|
r22 | opening_post = self.get(id=opening_post_id) | ||
neko259
|
r33 | |||
if opening_post.parent == NO_PARENT: | ||||
replies = self.filter(parent=opening_post_id) | ||||
neko259
|
r5 | |||
neko259
|
r33 | thread = [opening_post] | ||
thread.extend(replies) | ||||
neko259
|
r17 | |||
neko259
|
r33 | return thread | ||
neko259
|
r5 | |||
neko259
|
r8 | def exists(self, post_id): | ||
neko259
|
r22 | posts = self.filter(id=post_id) | ||
neko259
|
r8 | |||
neko259
|
r10 | return len(posts) > 0 | ||
neko259
|
r8 | |||
neko259
|
r28 | def _delete_old_threads(self): | ||
""" | ||||
Preserves maximum thread count. If there are too many threads, | ||||
delete the old ones. | ||||
""" | ||||
# TODO Try to find a better way to get the active thread count. | ||||
# 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. | ||||
threads = self.get_threads() | ||||
thread_count = len(threads) | ||||
if thread_count > settings.MAX_THREAD_COUNT: | ||||
num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT | ||||
old_threads = threads[-num_threads_to_delete:] | ||||
for thread in old_threads: | ||||
self.delete_post(thread) | ||||
neko259
|
r38 | def _bump_thread(self, thread_id): | ||
thread = self.get(id=thread_id) | ||||
replies_count = len(self.get_thread(thread_id)) | ||||
if replies_count <= settings.MAX_POSTS_PER_THREAD: | ||||
thread.last_edit_time = timezone.now() | ||||
thread.save() | ||||
neko259
|
r5 | |||
neko259
|
r33 | class TagManager(models.Manager): | ||
def get_not_empty_tags(self): | ||||
neko259
|
r34 | all_tags = self.all().order_by('name') | ||
neko259
|
r33 | tags = [] | ||
for tag in all_tags: | ||||
if not tag.is_empty(): | ||||
tags.append(tag) | ||||
return tags | ||||
neko259
|
r4 | class Tag(models.Model): | ||
""" | ||||
A tag is a text node assigned to the post. The tag serves as a board | ||||
section. There can be multiple tags for each message | ||||
""" | ||||
neko259
|
r33 | objects = TagManager() | ||
neko259
|
r22 | name = models.CharField(max_length=100) | ||
neko259
|
r28 | # TODO Connect the tag to its posts to check the number of threads for | ||
# the tag. | ||||
neko259
|
r4 | |||
neko259
|
r31 | def __unicode__(self): | ||
return self.name | ||||
neko259
|
r32 | def is_empty(self): | ||
neko259
|
r33 | return self.get_post_count() == 0 | ||
def get_post_count(self): | ||||
neko259
|
r32 | posts_with_tag = Post.objects.get_threads(tag=self) | ||
neko259
|
r33 | return len(posts_with_tag) | ||
neko259
|
r32 | |||
neko259
|
r8 | |||
neko259
|
r2 | class Post(models.Model): | ||
neko259
|
r4 | """A post is a message.""" | ||
neko259
|
r0 | |||
neko259
|
r2 | objects = PostManager() | ||
neko259
|
r0 | |||
neko259
|
r29 | title = models.CharField(max_length=50) | ||
neko259
|
r2 | pub_time = models.DateTimeField() | ||
neko259
|
r39 | text = MarkupField(default_markup_type='markdown', escape_html=True) | ||
neko259
|
r29 | image = thumbs.ImageWithThumbsField(upload_to=update_image_filename, | ||
neko259
|
r22 | blank=True, sizes=((200, 150),)) | ||
neko259
|
r2 | poster_ip = models.IPAddressField() | ||
poster_user_agent = models.TextField() | ||||
parent = models.BigIntegerField() | ||||
neko259
|
r7 | tags = models.ManyToManyField(Tag) | ||
neko259
|
r25 | last_edit_time = models.DateTimeField() | ||
neko259
|
r0 | |||
def __unicode__(self): | ||||
neko259
|
r40 | return self.title + ' (' + self.text.raw + ')' | ||
Ilyas
|
r9 | |||
neko259
|
r30 | def _get_replies(self): | ||
return Post.objects.filter(parent=self.id) | ||||
def get_reply_count(self): | ||||
return len(self._get_replies()) | ||||
def get_images_count(self): | ||||
images_count = 1 if self.image else 0 | ||||
for reply in self._get_replies(): | ||||
if reply.image: | ||||
images_count += 1 | ||||
return images_count | ||||
def get_gets_count(self): | ||||
gets_count = 1 if self.is_get() else 0 | ||||
for reply in self._get_replies(): | ||||
if reply.is_get(): | ||||
gets_count += 1 | ||||
return gets_count | ||||
def is_get(self): | ||||
"""If the post has pretty id (1, 1000, 77777), than it is called GET""" | ||||
neko259
|
r39 | first = self.id == 1 | ||
neko259
|
r30 | |||
neko259
|
r39 | # TODO Compile regexes | ||
neko259
|
r31 | |||
neko259
|
r39 | id_str = str(self.id) | ||
neko259
|
r42 | pretty = re.match(r'^\d(0)+$', id_str) | ||
same_digits = re.match(r'^(.)\1+$', id_str) | ||||
neko259
|
r39 | return first or pretty or same_digits | ||
neko259
|
r31 | |||
Ilyas
|
r9 | |||
neko259
|
r11 | class Admin(models.Model): | ||
Ilyas
|
r9 | """ | ||
Model for admin users | ||||
""" | ||||
neko259
|
r22 | name = models.CharField(max_length=100) | ||
password = models.CharField(max_length=100) | ||||
Ilyas
|
r9 | |||
def __unicode__(self): | ||||
return self.name + '/' + '*' * len(self.password) | ||||