Commits

Artur Barseghyan committed 27dfb2f Draft

initial

  • Participants

Comments (0)

Files changed (11)

+syntax: glob
+*.pyc
+.hgignore~
+setup.py~
+4fbcb1d4c67deaa8a9cc229d912c92e0ac885152 0.2
+AUTHOR
+    Artur Barseghyan (artur.barseghyan@gmail.com)
+
+DESCRIPTION
+    The idea is to generate thumbnails of external URLs. SORL does brilliant generation of thumbnails, but it does not 
+    work with external URLs. So we need to have a template tag, which will download an image and return its' relative 
+    URL in the system.
+
+REQUIREMENTS
+    * Image filename shall be an md5 of its' absolute URL.
+    * Before downloading the image given - check first if it does not exist in the system yet and not new (we can 
+    check file local time and if it is for example 1 month old we can download it again).
+    * When needs to be downloaded, download and save locally in a separate folder.
+    * We need to have certain application settings in settings.py
+
+INSTALLATION
+    You may install eximagination with pip install
+        pip install -e hg+http://bitbucket.org/barseghyanartur/eximagination#egg=eximagination
+
+    Alternatively, you may copy the eximagination directory to your project directory or install it in your virtual
+    environment.
+
+    Make sure to have the right path to media root defined in eximagination specific constants (either in defaults.py
+    or settings.py).
+
+    Add 'eximagination' to INSTALLED_APPS of settings.py.
+
+    Example settings.py
+        # extimagination options
+        EXIMAGINATION_MEDIA_ROOT = PROJECT_DIR('media/external_images/')
+        EXIMAGINATION_MEDIA_URL = '/media/external_images'
+        EXIMAGINATION_MEDIA_RELATIVE_ROOT = 'external_images/'
+        EXIMAGINATION_DEBUG = True
+
+    Example defaults.py
+        DEBUG = False
+        MEDIA_ROOT = PROJECT_DIR('media/external_images')
+        MEDIA_URL = '/media/external_images/'
+        MEDIA_RELATIVE_ROOT = 'external_images/'
+
+    USAGE
+        {% eximaginate 'http://www.google.com/intl/en/images/logo.gif' %}
+
+        or
+
+        {% eximaginate 'http://www.google.com/intl/en/images/logo.gif' as original %}
+
+    In both cases there are two additional context variables added:
+        ei_width - Width of the image
+        ei_height - Height of the image
+urllib2
+PIL>=1.1.7
+"""
+AUTHOR: Artur Barseghyan
+    (artur.barseghyan@gmail.com)
+DESCRIPTION
+    Setup file for Eximagination application. See "readme.txt" file for more.
+"""
+
+import os
+from setuptools import setup, find_packages
+
+readme = open(os.path.join(os.path.dirname(__file__), 'readme.txt')).read()
+
+version = '0.2'
+
+setup(
+    name='eximagination',
+    version=version,
+    description=("Eximagination package for copying external images in tempalate tags and storing them locally."),
+    long_description = readme,
+    classifiers=[
+        "Framework :: Django",
+        "Programming Language :: Python",
+        "Environment :: Web Environment",
+        'Intended Audience :: Developers',
+        'Operating System :: OS Independent',
+    ],
+    keywords='eximagination, django, external images, app, python',
+    author='Artur Barseghyan',
+    author_email='artur.barseghyan@gmail.com',
+    url='https://bitbucket.org/barseghyanartur/eximagination',
+    package_dir={'':'src'},
+    packages=find_packages(where='./src'),
+)

src/eximagination/__init__.py

Empty file added.

src/eximagination/conf.py

+from django.conf import settings
+from eximagination import defaults
+
+def get_setting(setting, override=None):
+    """
+    Get a setting from eximagination conf module, falling back to the default.
+
+    If override is not None, it will be used instead of the setting.
+    """
+    if override is not None:
+        return override
+    if hasattr(settings, 'EXIMAGINATION_%s' % setting):
+        return getattr(settings, 'EXIMAGINATION_%s' % setting)
+    else:
+        return getattr(defaults, setting)

src/eximagination/defaults.py

+import os
+PROJECT_DIR = lambda base : os.path.join(os.path.dirname(__file__), base).replace('\\','/')
+
+DEBUG = False
+MEDIA_ROOT = PROJECT_DIR('media/external_images')
+MEDIA_URL = '/media/external_images/'
+MEDIA_RELATIVE_ROOT = 'external_images/'

src/eximagination/templatetags/__init__.py

Empty file added.

src/eximagination/templatetags/eximaginate.py

+# Usage example:
+#   {% eximaginate photo as image %}
+#   {{ image }}
+# Note: Eximagination will automatically add "ei_width" and "ei_height" variables to the content - those are the
+# original width and height values of the obtained image.
+
+from django import template
+
+from eximagination.conf import get_setting
+from eximagination.utils import _obtain_image
+
+register = Library()
+
+DEBUG = get_setting('DEBUG') # Set back to settings from DEBUG.
+MEDIA_ROOT = get_setting('MEDIA_ROOT')
+MEDIA_URL = get_setting('MEDIA_URL')
+MEDIA_RELATIVE_ROOT = get_setting('MEDIA_RELATIVE_ROOT')
+
+class EximaginateNode(template.Node):
+    def __init__(self, source_var, force_update, context_name):
+        self.source_var = template.Variable(source_var)
+        self.force_update = bool(force_update)
+        self.context_name = context_name
+
+    def render(self, context):
+        # Note that this isn't a global constant because we need to change the
+        # value for tests.
+        try:
+            file_data = _obtain_image(image_source=self.source_var.resolve(context),
+                                      save_to=MEDIA_ROOT,
+                                      media_url=MEDIA_RELATIVE_ROOT,
+                                      force_update=self.force_update)
+            filename = file_data[0]
+            context['ei_width'] = file_data[1] # Original width of the obtained image
+            context['ei_height'] = file_data[2] # Original height of the obtained image
+            if self.context_name is None:
+                return filename
+
+            # We need to get here so we don't have old values in the context
+            # variable.
+            context[self.context_name] = filename
+        except:
+            pass
+
+        return ''
+
+@register.tag
+def eximaginate(parser, token):
+    """
+    Copies an external image locally.
+
+    To just output the absolute url to the thumbnail::
+
+        {% eximaginate 'http://www.google.com/intl/en/images/logo.gif' %}
+
+    To put the the downloaded image URL on the context instead of just rendering
+    the relative url, finish the tag with ``as [context_var_name]``::
+
+        {% eximaginate 'http://www.google.com/intl/en/images/logo.gif' as original %}
+
+    Eximagination will automatically add "ei_width" and "ei_height" variables to the content - those are the original
+    width and height values of the obtained image.
+
+    TODO: Implement force_update feature (some extra tokens parsing)
+    """
+    arguments = token.split_contents()
+    # Check to see if we're setting to a context variable.
+
+    if len(arguments) < 1:
+        raise template.TemplateSyntaxError, "%r tag requires at least one argument" % token.contents.split()[0]
+
+    # Get arguments
+    if 4 == len(arguments):
+        source_var = arguments[1]
+        force_update = False # At the moment force_update is not implemented
+        context_name = arguments[3]
+    elif 2 == len(arguments):
+        source_var = arguments[1]
+        force_update = False # At the moment force_update is not implemented
+        context_name = ''
+    else:
+        raise template.TemplateSyntaxError, "%r wrong number arguments" % token.contents.split()[0]
+
+    # If the size argument was a correct static format, wrap it in quotes so
+    # that it is compiled correctly.
+    return EximaginateNode(source_var, force_update, context_name)

src/eximagination/utils.py

+import urllib2
+import os
+import hashlib
+from PIL import Image
+import StringIO
+import glob
+
+def _obtain_image(image_source='', save_to='', media_url='', force_update=False):
+    """
+    Gets an image from absolute url and saves it locally. Checks wheither image already exists in local directoru
+    and only if not - tries to download it and saves it. Validates validity of the image (relying on PIL Image
+    class validation).
+
+    @param str image_source
+    @param str save_to
+    @param str media_url
+    @param bool force_update
+    @return list: (relative_url_of_the_image, original_image_width, original_image_height)
+    """
+    # First we check if any image with such name (without extention) exists in our local external images directory.
+    try:
+        filename = os.path.basename(glob.glob(os.path.join(save_to, hashlib.md5(image_source).hexdigest()) + '.*')[0])
+        im = Image.open(os.path.join(save_to, filename)) # Feed the image to PIL to get width and height
+        return (os.path.join(media_url, filename), im.size[0], im.size[1])
+    except:
+        pass
+
+    # If nothing was found in local directory - we try to load it and save it.
+    try:
+        # Loading the file of unknown type into memory. We don't save the image locally before it's validated.
+        opener = urllib2.build_opener()
+        page = opener.open(image_source)
+        external_image = page.read()
+
+        # Loading into PIL image in order to check if image is a proper image as well as to detect its' extention.
+        # This is probably the best way to validate the image and get its' extention, since PIL Image throws an
+        # exception when trying to load not a proper image.
+        im = Image.open(StringIO.StringIO(external_image))
+
+        # This is our filename
+        filename = hashlib.md5(image_source).hexdigest() + '.' + im.format.lower()
+
+        # If filename already exists - returning the filename
+        filename_full_path = os.path.join(save_to, filename)
+        if os.path.exists(filename_full_path) and os.path.isfile(filename_full_path) and not force_update:
+            return (os.path.join(media_url, filename), im.size[0], im.size[1])
+
+        # Open file for binary write and save the image.
+        fout = open(filename_full_path, "wb")
+        fout.write(external_image)
+        fout.close()
+        return (os.path.join(media_url, filename), im.size[0], im.size[1])
+    except Exception, e:
+        if DEBUG:
+            raise Exception("Wrong image type or can load the image")
+        else:
+            return ''