summaryrefslogtreecommitdiffstats
path: root/bn_django/photos/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'bn_django/photos/models.py')
-rw-r--r--bn_django/photos/models.py283
1 files changed, 283 insertions, 0 deletions
diff --git a/bn_django/photos/models.py b/bn_django/photos/models.py
new file mode 100644
index 0000000..33b2e0a
--- /dev/null
+++ b/bn_django/photos/models.py
@@ -0,0 +1,283 @@
+from django.db import models
+from django.utils.translation import gettext_lazy as _
+import django.contrib.auth.models as auth
+from django.conf import settings
+from django.dispatch import dispatcher
+from django.db.models import signals
+
+import os, os.path
+import Image
+
+# Handle settings here
+try:
+ STOCKPHOTO_BASE = settings.STOCKPHOTO_BASE.strip('/')
+except AttributeError:
+ STOCKPHOTO_BASE='photos'
+
+try:
+ STOCKPHOTO_URL = settings.STOCKPHOTO_URL
+except AttributeError:
+ STOCKPHOTO_URL='/photos'
+if STOCKPHOTO_URL[-1] == '/':
+ STOCKPHOTO_URL=STOCKPHOTO_URL[:-1]
+
+try:
+ ADMIN_URL = settings.ADMIN_URL
+except AttributeError:
+ ADMIN_URL='/admin'
+if ADMIN_URL[-1] == '/':
+ ADMIN_URL=ADMIN_URL[:-1]
+
+# Create your models here.
+class Gallery(models.Model):
+ title = models.CharField(_("title"), maxlength=80)
+ slug = models.SlugField(prepopulate_from=("title",))
+ date = models.DateField(_("publication date"), auto_now_add=True)
+ created = models.ForeignKey(auth.User,
+ verbose_name=_("gallery created by")
+ )
+ display_width = models.IntegerField(
+ _("width to display full images"),
+ default=640)
+ display_height = models.IntegerField(
+ _("height to display full images"),
+ default=480)
+ thumbnail_width = models.IntegerField(
+ _("width to display thumbnails"),
+ default=150)
+ thumbnail_height = models.IntegerField(
+ _("height to display thumbnails"),
+ default=100)
+
+ class Meta:
+ get_latest_by = 'date'
+
+ class Admin:
+ ordering = ['date']
+
+ def __str__(self):
+ return self.title
+ def get_absolute_url(self):
+ return "%s/%d/" % (STOCKPHOTO_URL, self.id)
+ def get_admin_url(self):
+ return "%s/photos/gallery/%d/" % (ADMIN_URL, self.id)
+ def was_published_today(self):
+ return self.date.date() == datetime.date.today()
+
+ def first(self):
+ try:
+ return self.photo_set.all()[0]
+ except IndexError:
+ return None
+ def update_thumbs(self):
+ for photo in self.photo_set.all():
+ photo.create_disp()
+ photo.create_thumb()
+
+class Photo(models.Model):
+ # import os, os.path, Image
+ image = models.ImageField(_("Photograph"),
+ upload_to= STOCKPHOTO_BASE + "/%Y/%m/%d/")
+ title = models.CharField(_("title"), maxlength=80)
+ desc = models.TextField(_("description"), blank=True)
+ gallery = models.ForeignKey(Gallery)
+ photographer = models.CharField(_("photographer"), maxlength=80,
+ blank=True)
+ date = models.DateField(_("date photographed"), blank=True, null=True)
+ extra = models.TextField(_("any extra information about the photo"),
+ blank=True)
+ class META:
+ get_latest_by = ['date']
+
+ class Admin:
+ ordering = ['date']
+
+ def __str__(self):
+ return self.title
+
+ def delete_thumbnails(self):
+ """Remove thumbnail and display-sized images when deleting.
+
+ This may fail if, for example, they don't exist, so it should
+ fail silently. It may not be a good idea to delete the
+ original, as the user may not understand that deleting it from
+ the gallery deletes it from the filesystem, so currently we
+ don't do that.
+
+ """
+ try:
+ os.unlink(self.thumbpath())
+ except (IOError, OSError):
+ pass
+ try:
+ os.unlink(self.disppath())
+ except (IOError, OSError):
+ pass
+ # Deleting the original might be a bad thing.
+ #os.unlink(self.fullpath())
+
+ def get_absolute_url(self):
+ return "%s/detail/%d/" % (STOCKPHOTO_URL, self.id)
+
+ def get_admin_url(self):
+ return "%s/photos/photos/%d/" % (ADMIN_URL, self.id)
+
+ def thumbpath(self):
+ """Path to the thumbnail
+ """
+ photobase = self.image[len(STOCKPHOTO_BASE)+1:]
+ return os.path.join( settings.MEDIA_ROOT, STOCKPHOTO_BASE,
+ "cache", "thumbs", photobase)
+
+ def thumburl(self):
+ """URL to the thumbnail
+ """
+ photobase = self.image[len(STOCKPHOTO_BASE)+1:]
+ if settings.MEDIA_URL.endswith('/'):
+ return settings.MEDIA_URL + STOCKPHOTO_BASE + \
+ "/cache/thumbs/" + photobase
+ return settings.MEDIA_URL + '/' + STOCKPHOTO_BASE + \
+ "/cache/thumbs/" + photobase
+
+
+ def disppath(self):
+ photobase = self.image[len(STOCKPHOTO_BASE)+1:]
+ return os.path.join( settings.MEDIA_ROOT, STOCKPHOTO_BASE,
+ "cache", photobase)
+
+ def dispurl(self):
+ photobase = self.image[len(STOCKPHOTO_BASE)+1:]
+ if settings.MEDIA_URL.endswith('/'):
+ return settings.MEDIA_URL + STOCKPHOTO_BASE + "/cache/" \
+ + photobase
+ return settings.MEDIA_URL + '/' + STOCKPHOTO_BASE + \
+ "/cache/" + photobase
+
+ def fullpath(self):
+ if self.image.startswith(os.path.sep):
+ return self.image
+ return os.path.join(settings.MEDIA_ROOT, self.image)
+
+ def fullurl(self):
+ if self.image.startswith(os.path.sep):
+ # Shouldn't happen anymore
+ return (settings.MEDIA_URL +
+ self.image[len(settings.MEDIA_ROOT):])
+ else:
+ if settings.MEDIA_URL.endswith('/'):
+ return settings.MEDIA_URL + self.image
+ return settings.MEDIA_URL + '/' + self.image
+
+
+ def next(self):
+ '''Return id of 'next' photo in the same gallery or None if at
+ the end.'''
+ # we could probably be more clever here by using the new nifty
+ # db access filters and queries, but for now, this is good enough
+ photo_list = [x for x in self.gallery.photo_set.all()]
+ ind = photo_list.index(self)
+ if (ind +1) == len(photo_list):
+ return None
+ else:
+ return photo_list[ind + 1]
+
+ def prev(self):
+ """Return id of 'previous' photo in the same gallery or None
+ if at the beginning
+ """
+ photo_list = [x for x in self.gallery.photo_set.all()]
+ ind = photo_list.index(self)
+ if ind == 0:
+ return False
+ else:
+ return photo_list[ind - 1]
+
+ def full_exists(self):
+ return os.path.exists( self.fullpath() )
+
+ def disp_exists(self):
+ return os.path.exists( self.disppath() )
+
+ def thumb_exists(self):
+ return os.path.exists( self.thumbpath() )
+
+ def create_disp(self):
+ im = Image.open( self.fullpath() )
+ format = im.format
+ # create the path for the display image
+ disp_path = self.disppath()
+ disp_dir = os.path.dirname(disp_path)
+ if not os.path.exists(disp_dir):
+ os.makedirs(disp_dir, 0775)
+
+ # Make a copy of the image, scaled, if needed.
+ maxwidth = self.gallery.display_width
+ maxheight = self.gallery.display_height
+ width, height = im.size
+ if (width > maxwidth) and width > height:
+ scale = float(maxwidth)/width
+ width = int(width * scale)
+ height = int(height * scale)
+ newim = im.resize( (width, height), Image.ANTIALIAS )
+ elif (height > maxheight) and height >= width:
+ scale = float(maxheight)/height
+ width = int(width * scale)
+ height = int(height * scale)
+ newim = im.resize( (width, height), Image.ANTIALIAS )
+ else:
+ newim = im
+ newim.save(disp_path, format)
+
+ def create_thumb(self):
+ im = Image.open( self.fullpath() )
+ format = im.format
+ # create the path for the thumbnail image
+ thumb_path = self.thumbpath()
+ thumb_dir = os.path.dirname(thumb_path)
+ if not os.path.exists(thumb_dir):
+ os.makedirs(thumb_dir, 0775)
+
+ # Make a copy of the image, scaled, if needed.
+ maxwidth = self.gallery.thumbnail_width
+ maxheight = self.gallery.thumbnail_height
+ width, height = im.size
+ if (width > maxwidth) and (width > height):
+ scale = float(maxwidth)/width
+ width = int(width * scale)
+ height = int(height * scale)
+ newim = im.resize( (width, height), Image.ANTIALIAS )
+ elif (height > maxheight):
+ scale = float(maxheight)/height
+ width = int(width * scale)
+ height = int(height * scale)
+ newim = im.resize( (width, height), Image.ANTIALIAS )
+ else:
+ newim = im
+ newim.save(thumb_path, format)
+
+ def build_display_images(self):
+ """Make thumbnail and display-sized images after saving.
+
+ For some reason, this may fail on a first pass (self.image may
+ be empty when this is called), but if we just let it fail
+ silently, it will apparently get called again and succeed.
+ """
+ if self.image:
+ if not self.thumb_exists():
+ self.create_thumb()
+ if not self.disp_exists():
+ self.create_disp()
+
+def build_display_images(sender, instance, signal, *args, **kwargs):
+ """Simple hook for save-after trigger
+ """
+ instance.build_display_images()
+def delete_thumbnails(sender, instance, signal, *args, **kwargs):
+ """Simple hook for pre-delete trigger.
+ """
+ instance.delete_thumbnails()
+
+dispatcher.connect(build_display_images, signal=signals.post_save,
+ sender=Photo)
+dispatcher.connect(delete_thumbnails, signal=signals.pre_delete,
+ sender=Photo)