##// END OF EJS Templates
Added tag 4.4.0 for changeset 5479c7f5f3e0
Added tag 4.4.0 for changeset 5479c7f5f3e0

File last commit:

r1866:fccf814a default
r1911:2e7d2864 default
Show More
__init__.py
158 lines | 5.1 KiB | text/x-python | PythonLexer
neko259
Do not rely on the md5 hash of the file, compare the file contents when searching for duplicate
r1824 from itertools import zip_longest
neko259
Store images as regular attachments instead of separate model
r1590 import boards
from boards.models import STATUS_ARCHIVE
from django.core.files.images import get_image_dimensions
neko259
Deduplicated upload_to method for images and file attachments
r1368 from django.db import models
neko259
Added support for different attachment types
r1273
neko259
Deduplicated file hash calculation method
r1305 from boards import utils
neko259
Store images as regular attachments instead of separate model
r1590 from boards.models.attachment.viewers import get_viewers, AbstractViewer, \
FILE_TYPES_IMAGE
neko259
Use mimetype instead of extensions for file type wherever it is used
r1866 from boards.utils import get_upload_filename, get_extension, cached_result, \
get_file_mimetype
neko259
Added support for different attachment types
r1273
class AttachmentManager(models.Manager):
def create_with_hash(self, file):
neko259
Deduplicated file hash calculation method
r1305 file_hash = utils.get_file_hash(file)
neko259
Add ability to check the file being uploaded for duplicates
r1855 attachment = self.get_existing_duplicate(file_hash, file)
neko259
Do not rely on the md5 hash of the file, compare the file contents when searching for duplicate
r1824 if not attachment:
neko259
Use mimetype instead of extensions for file type wherever it is used
r1866 file_type = get_file_mimetype(file)
neko259
Get mimetype for file in the form and use it for both images and attachments
r1371 attachment = self.create(file=file, mimetype=file_type,
hash=file_hash)
neko259
Added support for different attachment types
r1273
return attachment
neko259
Load URL if the file could not be loaded
r1660 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
neko259
Store images as regular attachments instead of separate model
r1590 def get_random_images(self, count, tags=None):
images = self.filter(mimetype__in=FILE_TYPES_IMAGE).exclude(
neko259
Fixed issue with random images
r1592 attachment_posts__thread__status=STATUS_ARCHIVE)
neko259
Store images as regular attachments instead of separate model
r1590 if tags is not None:
neko259
Fixed issue with random images
r1592 images = images.filter(attachment_posts__threads__tags__in=tags)
neko259
Store images as regular attachments instead of separate model
r1590 return images.order_by('?')[:count]
neko259
Add ability to check the file being uploaded for duplicates
r1855 def get_existing_duplicate(self, file_hash, file):
neko259
Do not rely on the md5 hash of the file, compare the file contents when searching for duplicate
r1824 """
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
neko259
Compare chunks of diffent sizes on a byte basis
r1857
file_chunks = file.chunks()
existing_file_chunks = existing_file.chunks()
if self._compare_chunks(file_chunks, existing_file_chunks):
neko259
If found several images with the same hash, use the equal one as duplicate not the first one with the hash
r1856 attachment = existing_attachment
return attachment
neko259
Do not rely on the md5 hash of the file, compare the file contents when searching for duplicate
r1824
neko259
Compare chunks of diffent sizes on a byte basis
r1857 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:
neko259
Fixed file comparison in different-size chunks
r1858 # Same size chunksm checked for equality previously
break
elif chunk1ended or chunk2ended:
# Different size chunks, not equal
equal = False
neko259
Compare chunks of diffent sizes on a byte basis
r1857 break
elif chunk1[position1] != chunk2[position2]:
neko259
Fixed file comparison in different-size chunks
r1858 # Different bytes, not equal
neko259
Compare chunks of diffent sizes on a byte basis
r1857 equal = False
break
else:
position1 += 1
position2 += 1
return equal
neko259
Added support for different attachment types
r1273
class Attachment(models.Model):
objects = AttachmentManager()
neko259
Properly order attachments across one post
r1759 class Meta:
app_label = 'boards'
ordering = ('id',)
neko259
Load URL if the file could not be loaded
r1660 file = models.FileField(upload_to=get_upload_filename, null=True)
neko259
Use mimetype instead of extensions for file type wherever it is used
r1866 mimetype = models.CharField(max_length=200, null=True)
neko259
Load URL if the file could not be loaded
r1660 hash = models.CharField(max_length=36, null=True)
neko259
Allow deleting aliases
r1750 alias = models.TextField(unique=True, null=True)
url = models.TextField(blank=True, default='')
neko259
Added support for different attachment types
r1273
def get_view(self):
file_viewer = None
neko259
Autodetect attachment viewers by getting all abstract viewer subclasses
r1286 for viewer in get_viewers():
neko259
Added support for different attachment types
r1273 if viewer.supports(self.mimetype):
neko259
Refactored attachment viewer getting method
r1306 file_viewer = viewer
neko259
Added support for different attachment types
r1273 break
if file_viewer is None:
neko259
Refactored attachment viewer getting method
r1306 file_viewer = AbstractViewer
neko259
Added support for different attachment types
r1273
neko259
Load URL if the file could not be loaded
r1660 return file_viewer(self.file, self.mimetype, self.hash, self.url).get_view()
neko259
Added support for different attachment types
r1273
neko259
Made some fields in post admin read-only. Show attachment as its url
r1389 def __str__(self):
neko259
Load URL if the file could not be loaded
r1660 return self.url or self.file.url
neko259
Store images as regular attachments instead of separate model
r1590
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):
neko259
Load URL if the file could not be loaded
r1660 if self.file:
if self.mimetype in FILE_TYPES_IMAGE:
return get_image_dimensions(self.file)
else:
return 200, 150
neko259
Store images as regular attachments instead of separate model
r1590
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):
neko259
Use mimetype instead of extensions for file type wherever it is used
r1866 size = 200, 150
neko259
Store images as regular attachments instead of separate model
r1590 if self.mimetype in FILE_TYPES_IMAGE:
preview_path = self.file.path.replace('.', '.200x150.')
neko259
Use mimetype instead of extensions for file type wherever it is used
r1866 try:
size = get_image_dimensions(preview_path)
except Exception:
pass
return size
neko259
Allow syncing URL attachments
r1706
def is_internal(self):
return self.url is None or len(self.url) == 0