##// END OF EJS Templates
Optimize cache key building
Optimize cache key building

File last commit:

r2006:127660f1 default
r2013:240e0e67 default
Show More
manager.py
291 lines | 10.2 KiB | text/x-python | PythonLexer
neko259
Moved post manager in a separate module
r1359 import logging
neko259
Unify thread and post creation into one method inside post manager, that can be called from almost anywhere (one step closer to ajax thread creation)
r1997 import re
neko259
Moved post manager in a separate module
r1359 from datetime import datetime, timedelta, date
from datetime import time as dtime
neko259
Added sticker pack functionality
r1951 from django.core.exceptions import PermissionDenied
neko259
Moved post manager in a separate module
r1359 from django.db import models, transaction
neko259
Added sticker pack functionality
r1951 from django.dispatch import Signal
neko259
Unify thread and post creation into one method inside post manager, that can be called from almost anywhere (one step closer to ajax thread creation)
r1997 from django.shortcuts import redirect
neko259
Moved post manager in a separate module
r1359 from django.utils import timezone
import boards
neko259
Added sticker pack functionality
r1951 from boards import utils
from boards.abstracts.exceptions import ArchiveException
neko259
Unify thread and post creation into one method inside post manager, that can be called from almost anywhere (one step closer to ajax thread creation)
r1997 from boards.abstracts.constants import REGEX_TAGS, REGEX_REPLY
neko259
Moved post manager in a separate module
r1359 from boards.mdx_neboard import Parser
neko259
Store images as regular attachments instead of separate model
r1590 from boards.models import Attachment
neko259
Added sticker pack functionality
r1951 from boards.models.attachment import StickerPack, AttachmentSticker
from boards.models.user import Ban
neko259
Moved post manager in a separate module
r1359
__author__ = 'neko259'
POSTS_PER_DAY_RANGE = 7
NO_IP = '0.0.0.0'
neko259
Added support for partial import: when the reply is imported but the original post is not, the reply will be connected after the original post is ready
r1588 post_import_deps = Signal()
neko259
Unify thread and post creation into one method inside post manager, that can be called from almost anywhere (one step closer to ajax thread creation)
r1997 FORM_TEXT = 'text'
FORM_TAGS = 'tags'
REFLINK_PREFIX = '>>'
neko259
Added support for partial import: when the reply is imported but the original post is not, the reply will be connected after the original post is ready
r1588
neko259
Moved post manager in a separate module
r1359 class PostManager(models.Manager):
@transaction.atomic
neko259
Add any number of files and URLs to post, not just one
r1753 def create_post(self, title: str, text: str, files=[], thread=None,
neko259
Removed multitread posts 'feature'
r1704 ip=NO_IP, tags: list=None,
neko259
Load URL if the file could not be loaded
r1660 tripcode='', monochrome=False, images=[],
neko259
Added sticker pack functionality
r1951 file_urls=[], stickerpack=False):
neko259
Moved post manager in a separate module
r1359 """
Creates new post
"""
neko259
Prevent posting into an archived thread
r1449 if thread is not None and thread.is_archived():
neko259
Moved exceptions to a separate module
r1602 raise ArchiveException('Cannot post into an archived thread')
neko259
Prevent posting into an archived thread
r1449
neko259
Added anonymous mode in which the board does not save poster IP addresses
r1362 if not utils.is_anonymous_mode():
is_banned = Ban.objects.filter(ip=ip).exists()
neko259
Added ability to create monochrome threads
r1434 else:
is_banned = False
neko259
Moved post manager in a separate module
r1359
if is_banned:
neko259
Do not allow banning users by not a moderator. Show 403 error when posting as a banned user, not 500
r1902 raise PermissionDenied()
neko259
Moved post manager in a separate module
r1359
if not tags:
tags = []
posting_time = timezone.now()
new_thread = False
if not thread:
thread = boards.models.thread.Thread.objects.create(
neko259
Added ability to create monochrome threads
r1434 bump_time=posting_time, last_edit_time=posting_time,
neko259
Added sticker pack functionality
r1951 monochrome=monochrome, stickerpack=stickerpack)
neko259
Moved post manager in a separate module
r1359 list(map(thread.tags.add, tags))
new_thread = True
pre_text = Parser().preparse(text)
post = self.create(title=title,
text=pre_text,
pub_time=posting_time,
poster_ip=ip,
thread=thread,
last_edit_time=posting_time,
tripcode=tripcode,
opening=new_thread)
logger = logging.getLogger('boards.post.create')
neko259
Cosmetic changes to post creation log
r1380 logger.info('Created post [{}] with text [{}] by {}'.format(post,
neko259
Show raw post text in the log when created post
r2006 post.get_raw_text(), post.poster_ip))
neko259
Moved post manager in a separate module
r1359
neko259
Add any number of files and URLs to post, not just one
r1753 for file in files:
neko259
Download attached filed to the post during sync
r1511 self._add_file_to_post(file, post)
neko259
Added image aliases to upload the same images (like "fake" or "gtfo")
r1500 for image in images:
neko259
Fixed updating post
r1604 post.attachments.add(image)
neko259
Add any number of files and URLs to post, not just one
r1753 for file_url in file_urls:
neko259
Load URL if the file could not be loaded
r1660 post.attachments.add(Attachment.objects.create_from_url(file_url))
neko259
Moved post manager in a separate module
r1359
neko259
Merged with default
r1320 post.set_global_id()
neko259
Moved post manager in a separate module
r1359
# Thread needs to be bumped only when the post is already created
if not new_thread:
thread.last_edit_time = posting_time
thread.bump()
thread.save()
neko259
Added sticker pack functionality
r1951 self._create_stickers(post)
neko259
Moved post manager in a separate module
r1359 return post
def delete_posts_by_ip(self, ip):
"""
Deletes all posts of the author with same IP
"""
posts = self.filter(poster_ip=ip)
for post in posts:
post.delete()
@utils.cached_result()
def get_posts_per_day(self) -> float:
"""
Gets average count of posts per day for the last 7 days
"""
day_end = date.today()
day_start = day_end - timedelta(POSTS_PER_DAY_RANGE)
day_time_start = timezone.make_aware(datetime.combine(
day_start, dtime()), timezone.get_current_timezone())
day_time_end = timezone.make_aware(datetime.combine(
day_end, dtime()), timezone.get_current_timezone())
posts_per_period = float(self.filter(
pub_time__lte=day_time_end,
pub_time__gte=day_time_start).count())
ppd = posts_per_period / POSTS_PER_DAY_RANGE
return ppd
neko259
Messages statistics for day/week/month
r1618 def get_post_per_days(self, days) -> int:
day_end = date.today() + timedelta(1)
day_start = day_end - timedelta(days)
day_time_start = timezone.make_aware(datetime.combine(
day_start, dtime()), timezone.get_current_timezone())
day_time_end = timezone.make_aware(datetime.combine(
day_end, dtime()), timezone.get_current_timezone())
return self.filter(
pub_time__lte=day_time_end,
pub_time__gte=day_time_start).count()
neko259
Merged with default
r1320 @transaction.atomic
def import_post(self, title: str, text: str, pub_time: str, global_id,
neko259
Parse tripcode for post sync
r1557 opening_post=None, tags=list(), files=list(),
neko259
Use update-time of a post instead of version
r1928 file_urls=list(), tripcode=None, last_edit_time=None):
neko259
Merged with default branch
r1360 is_opening = opening_post is None
if is_opening:
neko259
Merged with default
r1320 thread = boards.models.thread.Thread.objects.create(
bump_time=pub_time, last_edit_time=pub_time)
list(map(thread.tags.add, tags))
else:
thread = opening_post.get_thread()
neko259
Process updated posts from sync server
r1586 post = self.create(title=title,
text=text,
neko259
Merged with default
r1320 pub_time=pub_time,
poster_ip=NO_IP,
neko259
Use update-time of a post instead of version
r1928 last_edit_time=last_edit_time or pub_time,
neko259
Merged with default branch
r1360 global_id=global_id,
neko259
Sync fixes
r1386 opening=is_opening,
neko259
Process updated posts from sync server
r1586 thread=thread,
neko259
Use update-time of a post instead of version
r1928 tripcode=tripcode)
neko259
Merged with default
r1320
neko259
Download attached filed to the post during sync
r1511 for file in files:
self._add_file_to_post(file, post)
neko259
Parse url attachments by the sync manager. Parse each post in a separate transaction. Default number of posts in one GET request is 1
r1800 for file_url in file_urls:
post.attachments.add(Attachment.objects.create_from_url(file_url))
neko259
Download attached filed to the post during sync
r1511
neko259
Added support for partial import: when the reply is imported but the original post is not, the reply will be connected after the original post is ready
r1588 url_to_post = '[post]{}[/post]'.format(str(global_id))
replies = self.filter(text__contains=url_to_post)
for reply in replies:
neko259
Fix #3
r1613 post_import_deps.send(reply)
neko259
Added support for partial import: when the reply is imported but the original post is not, the reply will be connected after the original post is ready
r1588
neko259
Process updated posts from sync server
r1586 @transaction.atomic
def update_post(self, post, title: str, text: str, pub_time: str,
neko259
Use update-time of a post instead of version
r1928 tags=list(), files=list(), file_urls=list(), tripcode=None):
neko259
Process updated posts from sync server
r1586 post.title = title
post.text = text
post.pub_time = pub_time
post.tripcode = tripcode
post.save()
post.clear_cache()
post.attachments.clear()
for file in files:
self._add_file_to_post(file, post)
neko259
Parse url attachments by the sync manager. Parse each post in a separate transaction. Default number of posts in one GET request is 1
r1800 for file_url in file_urls:
post.attachments.add(Attachment.objects.create_from_url(file_url))
neko259
Process updated posts from sync server
r1586
thread = post.get_thread()
thread.tags.clear()
list(map(thread.tags.add, tags))
neko259
Unify thread and post creation into one method inside post manager, that can be called from almost anywhere (one step closer to ajax thread creation)
r1997 def create_from_form(self, request, form, opening_post, html_response=True):
ip = utils.get_client_ip(request)
data = form.cleaned_data
title = form.get_title()
text = data[FORM_TEXT]
files = form.get_files()
file_urls = form.get_file_urls()
images = form.get_images()
text = self._remove_invalid_links(text)
if opening_post:
post_thread = opening_post.get_thread()
monochrome = False
stickerpack = False
tags = []
else:
tags = data[FORM_TAGS]
monochrome = form.is_monochrome()
stickerpack = form.is_stickerpack()
post_thread = None
post = self.create_post(title=title, text=text, files=files,
thread=post_thread, ip=ip,
tripcode=form.get_tripcode(),
images=images, file_urls=file_urls,
monochrome=monochrome,
stickerpack=stickerpack, tags=tags)
if form.is_subscribe():
from boards.abstracts.settingsmanager import get_settings_manager
settings_manager = get_settings_manager(request)
settings_manager.add_or_read_fav_thread(
post_thread.get_opening_post())
if html_response:
return redirect(post.get_absolute_url())
else:
return post
neko259
Download attached filed to the post during sync
r1511 def _add_file_to_post(self, file, post):
neko259
Store images as regular attachments instead of separate model
r1590 post.attachments.add(Attachment.objects.create_with_hash(file))
neko259
Added sticker pack functionality
r1951
def _create_stickers(self, post):
thread = post.get_thread()
stickerpack_thread = thread.is_stickerpack()
if stickerpack_thread:
logger = logging.getLogger('boards.stickers')
if not post.is_opening():
has_title = len(post.title) > 0
has_one_attachment = post.attachments.count() == 1
opening_post = thread.get_opening_post()
valid_name = REGEX_TAGS.match(post.title)
if has_title and has_one_attachment and valid_name:
existing_sticker = AttachmentSticker.objects.filter(
name=post.get_title()).first()
attachment = post.attachments.first()
if existing_sticker:
existing_sticker.attachment = attachment
existing_sticker.save()
logger.info('Updated sticker {} with new attachment'.format(existing_sticker))
else:
try:
stickerpack = StickerPack.objects.get(
name=opening_post.get_title(), tripcode=post.tripcode)
sticker = AttachmentSticker.objects.create(
stickerpack=stickerpack, name=post.get_title(),
attachment=attachment)
logger.info('Created sticker {}'.format(sticker))
except StickerPack.DoesNotExist:
pass
else:
stickerpack, created = StickerPack.objects.get_or_create(
name=post.get_title(), tripcode=post.tripcode)
if created:
logger.info('Created stickerpack {}'.format(stickerpack))
neko259
Unify thread and post creation into one method inside post manager, that can be called from almost anywhere (one step closer to ajax thread creation)
r1997
def _remove_invalid_links(self, text):
"""
Replace invalid links in posts so that they won't be parsed.
Invalid links are links to non-existent posts
"""
for reply_number in re.finditer(REGEX_REPLY, text):
post_id = reply_number.group(1)
post = self.filter(id=post_id)
if not post.exists():
text = text.replace(REFLINK_PREFIX + post_id, post_id)
return text