##// END OF EJS Templates
Compare chunks of diffent sizes on a byte basis
Compare chunks of diffent sizes on a byte basis

File last commit:

r1857:87149f24 default
r1857:87149f24 default
Show More
__init__.py
148 lines | 4.8 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
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:
# FIXME Use full mimetype here, need to modify viewers too
file_type = get_extension(file.name)
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:
break
elif chunk1[position1] != chunk2[position2]:
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=50, 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):
if self.mimetype in FILE_TYPES_IMAGE:
preview_path = self.file.path.replace('.', '.200x150.')
return get_image_dimensions(preview_path)
else:
return 200, 150
def is_internal(self):
return self.url is None or len(self.url) == 0