forked from expo/troggle
Updates required to move to django 1.8
This commit is contained in:
parent
6f42bd51e1
commit
6984f66794
@ -10,7 +10,7 @@ from django.db.models import Min, Max
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from decimal import Decimal, getcontext
|
from decimal import Decimal, getcontext
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from imagekit.models import ImageModel
|
from imagekit.models import ProcessedImageField #ImageModel
|
||||||
from django.template import Context, loader
|
from django.template import Context, loader
|
||||||
import settings
|
import settings
|
||||||
getcontext().prec=2 #use 2 significant figures for decimal calculations
|
getcontext().prec=2 #use 2 significant figures for decimal calculations
|
||||||
@ -57,7 +57,7 @@ class TroggleModel(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
class TroggleImageModel(ImageModel):
|
class TroggleImageModel(models.Model):
|
||||||
new_since_parsing = models.BooleanField(default=False, editable=False)
|
new_since_parsing = models.BooleanField(default=False, editable=False)
|
||||||
|
|
||||||
def object_name(self):
|
def object_name(self):
|
||||||
|
@ -28,7 +28,8 @@ def reload_db():
|
|||||||
cursor.execute("CREATE DATABASE %s" % databasename)
|
cursor.execute("CREATE DATABASE %s" % databasename)
|
||||||
cursor.execute("ALTER DATABASE %s CHARACTER SET=utf8" % databasename)
|
cursor.execute("ALTER DATABASE %s CHARACTER SET=utf8" % databasename)
|
||||||
cursor.execute("USE %s" % databasename)
|
cursor.execute("USE %s" % databasename)
|
||||||
management.call_command('syncdb', interactive=False)
|
management.call_command('migrate', interactive=False)
|
||||||
|
#management.call_command('syncdb', interactive=False)
|
||||||
user = User.objects.create_user(expouser, expouseremail, expouserpass)
|
user = User.objects.create_user(expouser, expouseremail, expouserpass)
|
||||||
user.is_staff = True
|
user.is_staff = True
|
||||||
user.is_superuser = True
|
user.is_superuser = True
|
||||||
@ -218,7 +219,7 @@ if __name__ == "__main__":
|
|||||||
import_descriptions()
|
import_descriptions()
|
||||||
parse_descriptions()
|
parse_descriptions()
|
||||||
elif "survex" in sys.argv:
|
elif "survex" in sys.argv:
|
||||||
management.call_command('syncdb', interactive=False) # this sets the path so that import settings works in import_survex
|
#management.call_command('syncdb', interactive=False) # this sets the path so that import settings works in import_survex
|
||||||
import_survex()
|
import_survex()
|
||||||
elif "survexpos" in sys.argv:
|
elif "survexpos" in sys.argv:
|
||||||
management.call_command('syncdb', interactive=False) # this sets the path so that import settings works in import_survex
|
management.call_command('syncdb', interactive=False) # this sets the path so that import settings works in import_survex
|
||||||
|
@ -40,6 +40,7 @@ mkdir -p expofiles/surveyscans
|
|||||||
|
|
||||||
To start the containers run
|
To start the containers run
|
||||||
```bash
|
```bash
|
||||||
|
$ cd ~/expo/troggle/docker
|
||||||
$ docker-compose up
|
$ docker-compose up
|
||||||
```
|
```
|
||||||
You will now have a working troggle but with no data. To import the data you need to access the container run
|
You will now have a working troggle but with no data. To import the data you need to access the container run
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
"""
|
|
||||||
|
|
||||||
Django ImageKit
|
|
||||||
|
|
||||||
Author: Justin Driscoll <justin.driscoll@gmail.com>
|
|
||||||
Version: 0.2
|
|
||||||
|
|
||||||
"""
|
|
||||||
VERSION = "0.2"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
|||||||
""" Default ImageKit configuration """
|
|
||||||
|
|
||||||
from imagekit.specs import ImageSpec
|
|
||||||
from imagekit import processors
|
|
||||||
|
|
||||||
class ResizeThumbnail(processors.Resize):
|
|
||||||
width = 100
|
|
||||||
height = 50
|
|
||||||
crop = True
|
|
||||||
|
|
||||||
class EnhanceSmall(processors.Adjustment):
|
|
||||||
contrast = 1.2
|
|
||||||
sharpness = 1.1
|
|
||||||
|
|
||||||
class SampleReflection(processors.Reflection):
|
|
||||||
size = 0.5
|
|
||||||
background_color = "#000000"
|
|
||||||
|
|
||||||
class DjangoAdminThumbnail(ImageSpec):
|
|
||||||
access_as = 'admin_thumbnail'
|
|
||||||
processors = [ResizeThumbnail, EnhanceSmall, SampleReflection]
|
|
@ -1,17 +0,0 @@
|
|||||||
# Required PIL classes may or may not be available from the root namespace
|
|
||||||
# depending on the installation method used.
|
|
||||||
try:
|
|
||||||
import Image
|
|
||||||
import ImageFile
|
|
||||||
import ImageFilter
|
|
||||||
import ImageEnhance
|
|
||||||
import ImageColor
|
|
||||||
except ImportError:
|
|
||||||
try:
|
|
||||||
from PIL import Image
|
|
||||||
from PIL import ImageFile
|
|
||||||
from PIL import ImageFilter
|
|
||||||
from PIL import ImageEnhance
|
|
||||||
from PIL import ImageColor
|
|
||||||
except ImportError:
|
|
||||||
raise ImportError('ImageKit was unable to import the Python Imaging Library. Please confirm it`s installed and available on your current Python path.')
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,38 +0,0 @@
|
|||||||
from django.db.models.loading import cache
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
|
||||||
from optparse import make_option
|
|
||||||
from imagekit.models import ImageModel
|
|
||||||
from imagekit.specs import ImageSpec
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = ('Clears all ImageKit cached files.')
|
|
||||||
args = '[apps]'
|
|
||||||
requires_model_validation = True
|
|
||||||
can_import_settings = True
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
return flush_cache(args, options)
|
|
||||||
|
|
||||||
def flush_cache(apps, options):
|
|
||||||
""" Clears the image cache
|
|
||||||
|
|
||||||
"""
|
|
||||||
apps = [a.strip(',') for a in apps]
|
|
||||||
if apps:
|
|
||||||
print 'Flushing cache for %s...' % ', '.join(apps)
|
|
||||||
else:
|
|
||||||
print 'Flushing caches...'
|
|
||||||
|
|
||||||
for app_label in apps:
|
|
||||||
app = cache.get_app(app_label)
|
|
||||||
models = [m for m in cache.get_models(app) if issubclass(m, ImageModel)]
|
|
||||||
|
|
||||||
for model in models:
|
|
||||||
for obj in model.objects.all():
|
|
||||||
for spec in model._ik.specs:
|
|
||||||
prop = getattr(obj, spec.name(), None)
|
|
||||||
if prop is not None:
|
|
||||||
prop._delete()
|
|
||||||
if spec.pre_cache:
|
|
||||||
prop._create()
|
|
@ -1,136 +0,0 @@
|
|||||||
import os
|
|
||||||
from datetime import datetime
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.files.base import ContentFile
|
|
||||||
from django.db import models
|
|
||||||
from django.db.models.base import ModelBase
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
|
|
||||||
from imagekit import specs
|
|
||||||
from imagekit.lib import *
|
|
||||||
from imagekit.options import Options
|
|
||||||
from imagekit.utils import img_to_fobj
|
|
||||||
|
|
||||||
# Modify image file buffer size.
|
|
||||||
ImageFile.MAXBLOCK = getattr(settings, 'PIL_IMAGEFILE_MAXBLOCK', 256 * 2 ** 10)
|
|
||||||
|
|
||||||
# Choice tuples for specifying the crop origin.
|
|
||||||
# These are provided for convenience.
|
|
||||||
CROP_HORZ_CHOICES = (
|
|
||||||
(0, _('left')),
|
|
||||||
(1, _('center')),
|
|
||||||
(2, _('right')),
|
|
||||||
)
|
|
||||||
|
|
||||||
CROP_VERT_CHOICES = (
|
|
||||||
(0, _('top')),
|
|
||||||
(1, _('center')),
|
|
||||||
(2, _('bottom')),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ImageModelBase(ModelBase):
|
|
||||||
""" ImageModel metaclass
|
|
||||||
|
|
||||||
This metaclass parses IKOptions and loads the specified specification
|
|
||||||
module.
|
|
||||||
|
|
||||||
"""
|
|
||||||
def __init__(cls, name, bases, attrs):
|
|
||||||
parents = [b for b in bases if isinstance(b, ImageModelBase)]
|
|
||||||
if not parents:
|
|
||||||
return
|
|
||||||
user_opts = getattr(cls, 'IKOptions', None)
|
|
||||||
opts = Options(user_opts)
|
|
||||||
try:
|
|
||||||
module = __import__(opts.spec_module, {}, {}, [''])
|
|
||||||
except ImportError:
|
|
||||||
raise ImportError('Unable to load imagekit config module: %s' % \
|
|
||||||
opts.spec_module)
|
|
||||||
for spec in [spec for spec in module.__dict__.values() \
|
|
||||||
if isinstance(spec, type) \
|
|
||||||
and issubclass(spec, specs.ImageSpec) \
|
|
||||||
and spec != specs.ImageSpec]:
|
|
||||||
setattr(cls, spec.name(), specs.Descriptor(spec))
|
|
||||||
opts.specs.append(spec)
|
|
||||||
setattr(cls, '_ik', opts)
|
|
||||||
|
|
||||||
|
|
||||||
class ImageModel(models.Model):
|
|
||||||
""" Abstract base class implementing all core ImageKit functionality
|
|
||||||
|
|
||||||
Subclasses of ImageModel are augmented with accessors for each defined
|
|
||||||
image specification and can override the inner IKOptions class to customize
|
|
||||||
storage locations and other options.
|
|
||||||
|
|
||||||
"""
|
|
||||||
__metaclass__ = ImageModelBase
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
class IKOptions:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def admin_thumbnail_view(self):
|
|
||||||
if not self._imgfield:
|
|
||||||
return None
|
|
||||||
prop = getattr(self, self._ik.admin_thumbnail_spec, None)
|
|
||||||
if prop is None:
|
|
||||||
return 'An "%s" image spec has not been defined.' % \
|
|
||||||
self._ik.admin_thumbnail_spec
|
|
||||||
else:
|
|
||||||
if hasattr(self, 'get_absolute_url'):
|
|
||||||
return u'<a href="%s"><img src="%s"></a>' % \
|
|
||||||
(self.get_absolute_url(), prop.url)
|
|
||||||
else:
|
|
||||||
return u'<a href="%s"><img src="%s"></a>' % \
|
|
||||||
(self._imgfield.url, prop.url)
|
|
||||||
admin_thumbnail_view.short_description = _('Thumbnail')
|
|
||||||
admin_thumbnail_view.allow_tags = True
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _imgfield(self):
|
|
||||||
return getattr(self, self._ik.image_field)
|
|
||||||
|
|
||||||
def _clear_cache(self):
|
|
||||||
for spec in self._ik.specs:
|
|
||||||
prop = getattr(self, spec.name())
|
|
||||||
prop._delete()
|
|
||||||
|
|
||||||
def _pre_cache(self):
|
|
||||||
for spec in self._ik.specs:
|
|
||||||
if spec.pre_cache:
|
|
||||||
prop = getattr(self, spec.name())
|
|
||||||
prop._create()
|
|
||||||
|
|
||||||
def save(self, clear_cache=True, *args, **kwargs):
|
|
||||||
is_new_object = self._get_pk_val is None
|
|
||||||
super(ImageModel, self).save(*args, **kwargs)
|
|
||||||
if is_new_object:
|
|
||||||
clear_cache = False
|
|
||||||
spec = self._ik.preprocessor_spec
|
|
||||||
if spec is not None:
|
|
||||||
newfile = self._imgfield.storage.open(str(self._imgfield))
|
|
||||||
img = Image.open(newfile)
|
|
||||||
img = spec.process(img, None)
|
|
||||||
format = img.format or 'JPEG'
|
|
||||||
if format != 'JPEG':
|
|
||||||
imgfile = img_to_fobj(img, format)
|
|
||||||
else:
|
|
||||||
imgfile = img_to_fobj(img, format,
|
|
||||||
quality=int(spec.quality),
|
|
||||||
optimize=True)
|
|
||||||
content = ContentFile(imgfile.read())
|
|
||||||
newfile.close()
|
|
||||||
name = str(self._imgfield)
|
|
||||||
self._imgfield.storage.delete(name)
|
|
||||||
self._imgfield.storage.save(name, content)
|
|
||||||
if clear_cache and self._imgfield != '':
|
|
||||||
self._clear_cache()
|
|
||||||
self._pre_cache()
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)
|
|
||||||
self._clear_cache()
|
|
||||||
models.Model.delete(self)
|
|
@ -1,23 +0,0 @@
|
|||||||
# Imagekit options
|
|
||||||
from imagekit import processors
|
|
||||||
from imagekit.specs import ImageSpec
|
|
||||||
|
|
||||||
|
|
||||||
class Options(object):
|
|
||||||
""" Class handling per-model imagekit options
|
|
||||||
|
|
||||||
"""
|
|
||||||
image_field = 'image'
|
|
||||||
crop_horz_field = 'crop_horz'
|
|
||||||
crop_vert_field = 'crop_vert'
|
|
||||||
preprocessor_spec = None
|
|
||||||
cache_dir = 'cache'
|
|
||||||
save_count_as = None
|
|
||||||
cache_filename_format = "%(filename)s_%(specname)s.%(extension)s"
|
|
||||||
admin_thumbnail_spec = 'admin_thumbnail'
|
|
||||||
spec_module = 'imagekit.defaults'
|
|
||||||
|
|
||||||
def __init__(self, opts):
|
|
||||||
for key, value in opts.__dict__.iteritems():
|
|
||||||
setattr(self, key, value)
|
|
||||||
self.specs = []
|
|
@ -1,134 +0,0 @@
|
|||||||
""" Imagekit Image "ImageProcessors"
|
|
||||||
|
|
||||||
A processor defines a set of class variables (optional) and a
|
|
||||||
class method named "process" which processes the supplied image using
|
|
||||||
the class properties as settings. The process method can be overridden as well allowing user to define their
|
|
||||||
own effects/processes entirely.
|
|
||||||
|
|
||||||
"""
|
|
||||||
from imagekit.lib import *
|
|
||||||
|
|
||||||
class ImageProcessor(object):
|
|
||||||
""" Base image processor class """
|
|
||||||
@classmethod
|
|
||||||
def process(cls, image, obj=None):
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
class Adjustment(ImageProcessor):
|
|
||||||
color = 1.0
|
|
||||||
brightness = 1.0
|
|
||||||
contrast = 1.0
|
|
||||||
sharpness = 1.0
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def process(cls, image, obj=None):
|
|
||||||
for name in ['Color', 'Brightness', 'Contrast', 'Sharpness']:
|
|
||||||
factor = getattr(cls, name.lower())
|
|
||||||
if factor != 1.0:
|
|
||||||
image = getattr(ImageEnhance, name)(image).enhance(factor)
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
class Reflection(ImageProcessor):
|
|
||||||
background_color = '#FFFFFF'
|
|
||||||
size = 0.0
|
|
||||||
opacity = 0.6
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def process(cls, image, obj=None):
|
|
||||||
# convert bgcolor string to rgb value
|
|
||||||
background_color = ImageColor.getrgb(cls.background_color)
|
|
||||||
# copy orignial image and flip the orientation
|
|
||||||
reflection = image.copy().transpose(Image.FLIP_TOP_BOTTOM)
|
|
||||||
# create a new image filled with the bgcolor the same size
|
|
||||||
background = Image.new("RGB", image.size, background_color)
|
|
||||||
# calculate our alpha mask
|
|
||||||
start = int(255 - (255 * cls.opacity)) # The start of our gradient
|
|
||||||
steps = int(255 * cls.size) # the number of intermedite values
|
|
||||||
increment = (255 - start) / float(steps)
|
|
||||||
mask = Image.new('L', (1, 255))
|
|
||||||
for y in range(255):
|
|
||||||
if y < steps:
|
|
||||||
val = int(y * increment + start)
|
|
||||||
else:
|
|
||||||
val = 255
|
|
||||||
mask.putpixel((0, y), val)
|
|
||||||
alpha_mask = mask.resize(image.size)
|
|
||||||
# merge the reflection onto our background color using the alpha mask
|
|
||||||
reflection = Image.composite(background, reflection, alpha_mask)
|
|
||||||
# crop the reflection
|
|
||||||
reflection_height = int(image.size[1] * cls.size)
|
|
||||||
reflection = reflection.crop((0, 0, image.size[0], reflection_height))
|
|
||||||
# create new image sized to hold both the original image and the reflection
|
|
||||||
composite = Image.new("RGB", (image.size[0], image.size[1]+reflection_height), background_color)
|
|
||||||
# paste the orignal image and the reflection into the composite image
|
|
||||||
composite.paste(image, (0, 0))
|
|
||||||
composite.paste(reflection, (0, image.size[1]))
|
|
||||||
# return the image complete with reflection effect
|
|
||||||
return composite
|
|
||||||
|
|
||||||
|
|
||||||
class Resize(ImageProcessor):
|
|
||||||
width = None
|
|
||||||
height = None
|
|
||||||
crop = False
|
|
||||||
upscale = False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def process(cls, image, obj=None):
|
|
||||||
cur_width, cur_height = image.size
|
|
||||||
if cls.crop:
|
|
||||||
crop_horz = getattr(obj, obj._ik.crop_horz_field, 1)
|
|
||||||
crop_vert = getattr(obj, obj._ik.crop_vert_field, 1)
|
|
||||||
ratio = max(float(cls.width)/cur_width, float(cls.height)/cur_height)
|
|
||||||
resize_x, resize_y = ((cur_width * ratio), (cur_height * ratio))
|
|
||||||
crop_x, crop_y = (abs(cls.width - resize_x), abs(cls.height - resize_y))
|
|
||||||
x_diff, y_diff = (int(crop_x / 2), int(crop_y / 2))
|
|
||||||
box_left, box_right = {
|
|
||||||
0: (0, cls.width),
|
|
||||||
1: (int(x_diff), int(x_diff + cls.width)),
|
|
||||||
2: (int(crop_x), int(resize_x)),
|
|
||||||
}[crop_horz]
|
|
||||||
box_upper, box_lower = {
|
|
||||||
0: (0, cls.height),
|
|
||||||
1: (int(y_diff), int(y_diff + cls.height)),
|
|
||||||
2: (int(crop_y), int(resize_y)),
|
|
||||||
}[crop_vert]
|
|
||||||
box = (box_left, box_upper, box_right, box_lower)
|
|
||||||
image = image.resize((int(resize_x), int(resize_y)), Image.ANTIALIAS).crop(box)
|
|
||||||
else:
|
|
||||||
if not cls.width is None and not cls.height is None:
|
|
||||||
ratio = min(float(cls.width)/cur_width,
|
|
||||||
float(cls.height)/cur_height)
|
|
||||||
else:
|
|
||||||
if cls.width is None:
|
|
||||||
ratio = float(cls.height)/cur_height
|
|
||||||
else:
|
|
||||||
ratio = float(cls.width)/cur_width
|
|
||||||
new_dimensions = (int(round(cur_width*ratio)),
|
|
||||||
int(round(cur_height*ratio)))
|
|
||||||
if new_dimensions[0] > cur_width or \
|
|
||||||
new_dimensions[1] > cur_height:
|
|
||||||
if not cls.upscale:
|
|
||||||
return image
|
|
||||||
image = image.resize(new_dimensions, Image.ANTIALIAS)
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
class Transpose(ImageProcessor):
|
|
||||||
""" Rotates or flips the image
|
|
||||||
|
|
||||||
Method should be one of the following strings:
|
|
||||||
- FLIP_LEFT RIGHT
|
|
||||||
- FLIP_TOP_BOTTOM
|
|
||||||
- ROTATE_90
|
|
||||||
- ROTATE_270
|
|
||||||
- ROTATE_180
|
|
||||||
|
|
||||||
"""
|
|
||||||
method = 'FLIP_LEFT_RIGHT'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def process(cls, image, obj=None):
|
|
||||||
return image.transpose(getattr(Image, cls.method))
|
|
@ -1,119 +0,0 @@
|
|||||||
""" ImageKit image specifications
|
|
||||||
|
|
||||||
All imagekit specifications must inherit from the ImageSpec class. Models
|
|
||||||
inheriting from ImageModel will be modified with a descriptor/accessor for each
|
|
||||||
spec found.
|
|
||||||
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
from StringIO import StringIO
|
|
||||||
from imagekit.lib import *
|
|
||||||
from imagekit.utils import img_to_fobj
|
|
||||||
from django.core.files.base import ContentFile
|
|
||||||
|
|
||||||
class ImageSpec(object):
|
|
||||||
pre_cache = False
|
|
||||||
quality = 70
|
|
||||||
increment_count = False
|
|
||||||
processors = []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def name(cls):
|
|
||||||
return getattr(cls, 'access_as', cls.__name__.lower())
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def process(cls, image, obj):
|
|
||||||
processed_image = image.copy()
|
|
||||||
for proc in cls.processors:
|
|
||||||
processed_image = proc.process(processed_image, obj)
|
|
||||||
return processed_image
|
|
||||||
|
|
||||||
|
|
||||||
class Accessor(object):
|
|
||||||
def __init__(self, obj, spec):
|
|
||||||
self._img = None
|
|
||||||
self._obj = obj
|
|
||||||
self.spec = spec
|
|
||||||
|
|
||||||
def _get_imgfile(self):
|
|
||||||
format = self._img.format or 'JPEG'
|
|
||||||
if format != 'JPEG':
|
|
||||||
imgfile = img_to_fobj(self._img, format)
|
|
||||||
else:
|
|
||||||
imgfile = img_to_fobj(self._img, format,
|
|
||||||
quality=int(self.spec.quality),
|
|
||||||
optimize=True)
|
|
||||||
return imgfile
|
|
||||||
|
|
||||||
def _create(self):
|
|
||||||
if self._exists():
|
|
||||||
return
|
|
||||||
# process the original image file
|
|
||||||
fp = self._obj._imgfield.storage.open(self._obj._imgfield.name)
|
|
||||||
fp.seek(0)
|
|
||||||
fp = StringIO(fp.read())
|
|
||||||
try:
|
|
||||||
self._img = self.spec.process(Image.open(fp), self._obj)
|
|
||||||
# save the new image to the cache
|
|
||||||
content = ContentFile(self._get_imgfile().read())
|
|
||||||
self._obj._imgfield.storage.save(self.name, content)
|
|
||||||
except IOError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _delete(self):
|
|
||||||
self._obj._imgfield.storage.delete(self.name)
|
|
||||||
|
|
||||||
def _exists(self):
|
|
||||||
return self._obj._imgfield.storage.exists(self.name)
|
|
||||||
|
|
||||||
def _basename(self):
|
|
||||||
filename, extension = \
|
|
||||||
os.path.splitext(os.path.basename(self._obj._imgfield.name))
|
|
||||||
return self._obj._ik.cache_filename_format % \
|
|
||||||
{'filename': filename,
|
|
||||||
'specname': self.spec.name(),
|
|
||||||
'extension': extension.lstrip('.')}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return os.path.join(self._obj._ik.cache_dir, self._basename())
|
|
||||||
|
|
||||||
@property
|
|
||||||
def url(self):
|
|
||||||
self._create()
|
|
||||||
if self.spec.increment_count:
|
|
||||||
fieldname = self._obj._ik.save_count_as
|
|
||||||
if fieldname is not None:
|
|
||||||
current_count = getattr(self._obj, fieldname)
|
|
||||||
setattr(self._obj, fieldname, current_count + 1)
|
|
||||||
self._obj.save(clear_cache=False)
|
|
||||||
return self._obj._imgfield.storage.url(self.name)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def file(self):
|
|
||||||
self._create()
|
|
||||||
return self._obj._imgfield.storage.open(self.name)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def image(self):
|
|
||||||
if self._img is None:
|
|
||||||
self._create()
|
|
||||||
if self._img is None:
|
|
||||||
self._img = Image.open(self.file)
|
|
||||||
return self._img
|
|
||||||
|
|
||||||
@property
|
|
||||||
def width(self):
|
|
||||||
return self.image.size[0]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def height(self):
|
|
||||||
return self.image.size[1]
|
|
||||||
|
|
||||||
|
|
||||||
class Descriptor(object):
|
|
||||||
def __init__(self, spec):
|
|
||||||
self._spec = spec
|
|
||||||
|
|
||||||
def __get__(self, obj, type=None):
|
|
||||||
return Accessor(obj, self._spec)
|
|
@ -1,86 +0,0 @@
|
|||||||
import os
|
|
||||||
import tempfile
|
|
||||||
import unittest
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.files.base import ContentFile
|
|
||||||
from django.db import models
|
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
from imagekit import processors
|
|
||||||
from imagekit.models import ImageModel
|
|
||||||
from imagekit.specs import ImageSpec
|
|
||||||
from imagekit.lib import Image
|
|
||||||
|
|
||||||
|
|
||||||
class ResizeToWidth(processors.Resize):
|
|
||||||
width = 100
|
|
||||||
|
|
||||||
class ResizeToHeight(processors.Resize):
|
|
||||||
height = 100
|
|
||||||
|
|
||||||
class ResizeToFit(processors.Resize):
|
|
||||||
width = 100
|
|
||||||
height = 100
|
|
||||||
|
|
||||||
class ResizeCropped(ResizeToFit):
|
|
||||||
crop = ('center', 'center')
|
|
||||||
|
|
||||||
class TestResizeToWidth(ImageSpec):
|
|
||||||
access_as = 'to_width'
|
|
||||||
processors = [ResizeToWidth]
|
|
||||||
|
|
||||||
class TestResizeToHeight(ImageSpec):
|
|
||||||
access_as = 'to_height'
|
|
||||||
processors = [ResizeToHeight]
|
|
||||||
|
|
||||||
class TestResizeCropped(ImageSpec):
|
|
||||||
access_as = 'cropped'
|
|
||||||
processors = [ResizeCropped]
|
|
||||||
|
|
||||||
class TestPhoto(ImageModel):
|
|
||||||
""" Minimal ImageModel class for testing """
|
|
||||||
image = models.ImageField(upload_to='images')
|
|
||||||
|
|
||||||
class IKOptions:
|
|
||||||
spec_module = 'imagekit.tests'
|
|
||||||
|
|
||||||
|
|
||||||
class IKTest(TestCase):
|
|
||||||
""" Base TestCase class """
|
|
||||||
def setUp(self):
|
|
||||||
# create a test image using tempfile and PIL
|
|
||||||
self.tmp = tempfile.TemporaryFile()
|
|
||||||
Image.new('RGB', (800, 600)).save(self.tmp, 'JPEG')
|
|
||||||
self.tmp.seek(0)
|
|
||||||
self.p = TestPhoto()
|
|
||||||
self.p.image.save(os.path.basename('test.jpg'),
|
|
||||||
ContentFile(self.tmp.read()))
|
|
||||||
self.p.save()
|
|
||||||
# destroy temp file
|
|
||||||
self.tmp.close()
|
|
||||||
|
|
||||||
def test_setup(self):
|
|
||||||
self.assertEqual(self.p.image.width, 800)
|
|
||||||
self.assertEqual(self.p.image.height, 600)
|
|
||||||
|
|
||||||
def test_to_width(self):
|
|
||||||
self.assertEqual(self.p.to_width.width, 100)
|
|
||||||
self.assertEqual(self.p.to_width.height, 75)
|
|
||||||
|
|
||||||
def test_to_height(self):
|
|
||||||
self.assertEqual(self.p.to_height.width, 133)
|
|
||||||
self.assertEqual(self.p.to_height.height, 100)
|
|
||||||
|
|
||||||
def test_crop(self):
|
|
||||||
self.assertEqual(self.p.cropped.width, 100)
|
|
||||||
self.assertEqual(self.p.cropped.height, 100)
|
|
||||||
|
|
||||||
def test_url(self):
|
|
||||||
tup = (settings.MEDIA_URL, self.p._ik.cache_dir, 'test_to_width.jpg')
|
|
||||||
self.assertEqual(self.p.to_width.url, "%s%s/%s" % tup)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
# make sure image file is deleted
|
|
||||||
path = self.p.image.path
|
|
||||||
self.p.delete()
|
|
||||||
self.failIf(os.path.isfile(path))
|
|
@ -1,15 +0,0 @@
|
|||||||
""" ImageKit utility functions """
|
|
||||||
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
def img_to_fobj(img, format, **kwargs):
|
|
||||||
tmp = tempfile.TemporaryFile()
|
|
||||||
if format != 'JPEG':
|
|
||||||
try:
|
|
||||||
img.save(tmp, format, **kwargs)
|
|
||||||
return
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
img.save(tmp, format, **kwargs)
|
|
||||||
tmp.seek(0)
|
|
||||||
return tmp
|
|
@ -92,7 +92,7 @@ INSTALLED_APPS = (
|
|||||||
'troggle.profiles',
|
'troggle.profiles',
|
||||||
'troggle.core',
|
'troggle.core',
|
||||||
'troggle.flatpages',
|
'troggle.flatpages',
|
||||||
'troggle.imagekit',
|
'imagekit',
|
||||||
)
|
)
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = (
|
MIDDLEWARE_CLASSES = (
|
||||||
|
Loading…
Reference in New Issue
Block a user