##// END OF EJS Templates
Made quote button actually button so it is impossible to accidentally quote its text
Made quote button actually button so it is impossible to accidentally quote its text

File last commit:

r1866:fccf814a default
r1909:004a4ff8 default
Show More
__init__.py
158 lines | 5.1 KiB | text/x-python | PythonLexer
from itertools import zip_longest
import boards
from boards.models import STATUS_ARCHIVE
from django.core.files.images import get_image_dimensions
from django.db import models
from boards import utils
from boards.models.attachment.viewers import get_viewers, AbstractViewer, \
FILE_TYPES_IMAGE
from boards.utils import get_upload_filename, get_extension, cached_result, \
get_file_mimetype
class AttachmentManager(models.Manager):
def create_with_hash(self, file):
file_hash = utils.get_file_hash(file)
attachment = self.get_existing_duplicate(file_hash, file)
if not attachment:
file_type = get_file_mimetype(file)
attachment = self.create(file=file, mimetype=file_type,
hash=file_hash)
return attachment
def create_from_url(self, url):
existing = self.filter(url=url)
if len(existing) > 0:
attachment = existing[0]
else:
attachment = self.create(url=url)
return attachment
def get_random_images(self, count, tags=None):
images = self.filter(mimetype__in=FILE_TYPES_IMAGE).exclude(
attachment_posts__thread__status=STATUS_ARCHIVE)
if tags is not None:
images = images.filter(attachment_posts__threads__tags__in=tags)
return images.order_by('?')[:count]
def get_existing_duplicate(self, file_hash, file):
"""
Gets an attachment with the same file if one exists.
"""
existing = self.filter(hash=file_hash)
attachment = None
for existing_attachment in existing:
existing_file = existing_attachment.file
file_chunks = file.chunks()
existing_file_chunks = existing_file.chunks()
if self._compare_chunks(file_chunks, existing_file_chunks):
attachment = existing_attachment
return attachment
def _compare_chunks(self, chunks1, chunks2):
"""
Compares 2 chunks of different sizes (e.g. first chunk array contains
all data in 1 chunk, and other one -- in a multiple of smaller ones.
"""
equal = True
position1 = 0
position2 = 0
chunk1 = None
chunk2 = None
chunk1ended = False
chunk2ended = False
while True:
if not chunk1 or len(chunk1) <= position1:
try:
chunk1 = chunks1.__next__()
position1 = 0
except StopIteration:
chunk1ended = True
if not chunk2 or len(chunk2) <= position2:
try:
chunk2 = chunks2.__next__()
position2 = 0
except StopIteration:
chunk2ended = True
if chunk1ended and chunk2ended:
# Same size chunksm checked for equality previously
break
elif chunk1ended or chunk2ended:
# Different size chunks, not equal
equal = False
break
elif chunk1[position1] != chunk2[position2]:
# Different bytes, not equal
equal = False
break
else:
position1 += 1
position2 += 1
return equal
class Attachment(models.Model):
objects = AttachmentManager()
class Meta:
app_label = 'boards'
ordering = ('id',)
file = models.FileField(upload_to=get_upload_filename, null=True)
mimetype = models.CharField(max_length=200, null=True)
hash = models.CharField(max_length=36, null=True)
alias = models.TextField(unique=True, null=True)
url = models.TextField(blank=True, default='')
def get_view(self):
file_viewer = None
for viewer in get_viewers():
if viewer.supports(self.mimetype):
file_viewer = viewer
break
if file_viewer is None:
file_viewer = AbstractViewer
return file_viewer(self.file, self.mimetype, self.hash, self.url).get_view()
def __str__(self):
return self.url or self.file.url
def get_random_associated_post(self):
posts = boards.models.Post.objects.filter(attachments__in=[self])
return posts.order_by('?').first()
@cached_result()
def get_size(self):
if self.file:
if self.mimetype in FILE_TYPES_IMAGE:
return get_image_dimensions(self.file)
else:
return 200, 150
def get_thumb_url(self):
split = self.file.url.rsplit('.', 1)
w, h = 200, 150
return '%s.%sx%s.%s' % (split[0], w, h, split[1])
@cached_result()
def get_preview_size(self):
size = 200, 150
if self.mimetype in FILE_TYPES_IMAGE:
preview_path = self.file.path.replace('.', '.200x150.')
try:
size = get_image_dimensions(preview_path)
except Exception:
pass
return size
def is_internal(self):
return self.url is None or len(self.url) == 0