degal/folder.py
branchuse-distutils
changeset 41 3b1579a7bffb
parent 31 09776792a91c
child 44 533b7e8b5d3b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/degal/folder.py	Wed Jun 03 19:03:28 2009 +0300
@@ -0,0 +1,329 @@
+# DeGAL - A pretty simple web image gallery
+# Copyright (C) 2007 Tero Marttila
+# http://marttila.de/~terom/degal/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the
+# Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+
+import os, os.path
+
+import settings, image, utils, helpers, log
+from template import gallery as gallery_tpl
+from helpers import url_for_page
+
+def dirUp (count=1) :
+    """
+        Returns a relative path to the directly count levels above the current one
+    """
+
+    if not count :
+        return '.'
+
+    return os.path.join(*(['..']*count))
+    
+class Folder (object) :
+    def __init__ (self, name='.', parent=None) :
+        # the directory name, no trailing /
+        self.name = unicode(name.rstrip(os.sep))
+
+        # our parent Folder, or None
+        self.parent = parent
+
+        # the path to this dir, as a relative path to the root of the image gallery, always starts with .
+        if parent and name :
+            self.path = parent.pathFor(self.name)
+        else :
+            self.path = self.name
+
+        # the url-path to the index.html file
+        self.html_path = self.path
+        
+        # dict of fname -> Folder
+        self.subdirs = {}
+
+        # dict of fname -> Image
+        self.images = {}
+        
+        # our human-friendly title
+        self.title = None
+
+        # our long-winded description
+        self.descr = ''
+
+        # is this folder non-empty?
+        self.alive = None
+        
+        # self.images.values(), but sorted by filename
+        self.sorted_images = []
+        
+        # the ShortURL key to this dir
+        self.shorturl_code = None
+
+        # were we filtered out?
+        self.filtered = False
+   
+    def pathFor (self, *fnames) :
+        """
+            Return a root-relative path to the given path inside this dir
+        """
+        return os.path.join(self.path, *fnames)
+
+    def index (self, filters=None) :
+        """
+            Look for other dirs and images inside this dir. Filters must be either None,
+            whereupon all files will be included, or a dict of {filename -> next_filter}.
+            If given, only filenames that are present in the dict will be indexed, and in
+            the case of dirs, the next_filter will be passed on to that Folder's index
+            method.
+        """
+
+        if filters :
+            self.filtered = True
+        
+        # iterate through listdir
+        for fname in os.listdir(self.path) :
+            # the full filesystem path to it
+            fpath = self.pathFor(fname)
+            
+            # ignore dotfiles
+            if fname.startswith('.') :
+                log.debug("Skipping dotfile %s", fname)
+                continue
+            
+            # apply filters
+            if filters :
+                if fname in filters :
+                    next_filter = filters[fname]
+                else :
+                    log.debug("Skip `%s' as we have a filter", fname)
+                    continue
+            else :
+                next_filter = None
+                
+            # recurse into subdirs, but not thumbs/previews
+            if (os.path.isdir(fpath) 
+                and (fname not in (settings.THUMB_DIR, settings.PREVIEW_DIR))
+                and (self.parent or fname not in settings.ROOT_IGNORE)
+            ) :
+                log.down(fname)
+
+                f = Folder(fname, self)
+                
+                try :
+                    if f.index(next_filter) :   # recursion
+                        # if a subdir is alive, we are alive as well
+                        self.subdirs[fname] = f
+                        self.alive = True
+                except Exception, e :
+                    log.warning("skip - %s: %s" % (type(e), e))
+
+                log.up()
+
+            # handle images
+            elif os.path.isfile(fpath) and utils.isImage(fname) :
+                log.next(fname)
+                self.images[fname] = image.Image(self, fname)
+
+            # ignore everything else
+            else :
+                log.debug("Ignoring file %s", fname)
+        
+        # sort and link the images
+        if self.images :
+            self.alive = True
+
+            # sort the images
+            fnames = self.images.keys()
+            fnames.sort()
+
+            prev = None
+
+            # link
+            for fname in fnames :
+                img = self.images[fname]
+
+                img.prev = prev
+
+                if prev :
+                    prev.next = img
+
+                prev = img
+                
+                # add to the sorted images list
+                self.sorted_images.append(img)
+                
+        # figure out our title/ descr. Must be done before our parent dir is rendered (self.title)
+        title_path = self.pathFor(settings.TITLE_FILE)
+        
+        self.title, self.descr = utils.readTitleDescr(title_path)
+        
+        # default title for the root dir
+        if self.title or self.descr :
+            self.alive = True
+            pass # use what was in the title file
+            
+        elif not self.parent :
+            self.title = 'Index'
+
+        else :
+            self.title = self.name
+        
+        if not self.alive :
+            log.debug("Dir %s isn't alive" % self.path)
+
+        return self.alive
+
+    def getObjInfo (self) :
+        """
+            Metadata for shorturls2.db
+        """
+        return 'dir', self.path, ''
+
+    def breadcrumb (self, forImg=None) :
+        """
+            Returns a [(fname, title)] list of this dir's parent dirs
+        """
+
+        f = self
+        b = []
+        d = 0
+        
+        while f :
+            # functionality of the slightly-hacked-in variety
+            if f is self and forImg is not None :
+                url = helpers.url_for_page(self.getPageNumber(forImg))
+            else :
+                url = dirUp(d)
+                
+            b.insert(0, (url, f.title))
+
+            d += 1
+            f = f.parent
+        
+        return b
+        
+    def getPageNumber (self, img) :
+        """
+            Get the page number that the given image is on
+        """
+        
+        return self.sorted_images.index(img) // settings.IMAGE_COUNT
+
+    def countParents (self, acc=0) :
+        if self.parent :
+            return self.parent.countParents(acc+1)
+        else :
+            return acc
+    
+    def inRoot (self, *fnames) :
+        """
+            Return a relative URL from this dir to the given path in the root dir
+        """
+
+        c = self.countParents()
+
+        return utils.url_join(*((['..']*c) + list(fnames)))
+
+    def render (self) :
+        """
+            Render the index.html, Images, and recurse into subdirs
+        """
+        
+        # ded folders are skipped
+        if not self.alive :
+            # dead, skip, no output
+            return
+        
+        index_mtime = utils.mtime(self.pathFor("index.html"))
+        dir_mtime = utils.mtime(self.path)
+
+        # if this dir's contents were filtered out, then we can't render the index.html, as we aren't aware of all the images in here
+        if self.filtered :
+            log.warning("Dir `%s' contents were filtered, so we won't render the gallery index again", self.path)
+
+        elif index_mtime > dir_mtime :
+            # no changes, pass, ignored
+            pass
+
+        else :  
+            # create the thumb/preview dirs if needed
+            for dir in (settings.THUMB_DIR, settings.PREVIEW_DIR) :
+                path = self.pathFor(dir)
+
+                if not os.path.isdir(path) :
+                    log.info("mkdir %s", dir)
+                    os.mkdir(path)
+
+            # sort the subdirs
+            subdirs = self.subdirs.values()
+            subdirs.sort(key=lambda d: d.name)
+            
+            # paginate!
+            images = self.sorted_images
+            image_count = len(images)
+            pages = []
+            
+            while images :
+                pages.append(images[:settings.IMAGE_COUNT])
+                images = images[settings.IMAGE_COUNT:]
+
+            pagination_required = len(pages) > 1
+
+            if pagination_required :
+                log.info("%d pages @ %d images", len(pages), settings.IMAGE_COUNT)
+            elif not pages :
+                log.info("no images, render for subdirs")
+                pages = [[]]
+
+            for cur_page, images in enumerate(pages) :
+                if pagination_required and cur_page > 0 :
+                    shorturl = "%s/%s" % (self.shorturl_code, cur_page+1)
+                else :
+                    shorturl = self.shorturl_code
+                
+                # render to index.html
+                gallery_tpl.render_to(self.pathFor(url_for_page(cur_page)), 
+                    stylesheet_url               = self.inRoot('style.css'),
+                    title                        = self.title,
+                    breadcrumb                   = self.breadcrumb(),
+                    
+                    dirs                         = subdirs,
+                    images                       = images,
+                    
+                    num_pages                    = len(pages),
+                    cur_page                     = cur_page,
+                    
+                    description                  = self.descr,
+                    
+                    shorturl                     = self.inRoot('s', shorturl),
+                    shorturl_code                = shorturl,
+                )
+
+        # render images
+        image_count = len(self.sorted_images)
+        for i, img in enumerate(self.images.itervalues()) :
+            log.next("[%-4d/%4d] %s", i + 1, image_count, img.name)
+
+            img.render()
+        
+        # recurse into subdirs
+        for dir in self.subdirs.itervalues() :
+            log.down(dir.name)
+
+            dir.render()
+
+            log.up()
+