lib/folder.py
branchuse-distutils
changeset 41 3b1579a7bffb
parent 40 373392025533
child 42 146997912efb
equal deleted inserted replaced
40:373392025533 41:3b1579a7bffb
     1 # DeGAL - A pretty simple web image gallery
       
     2 # Copyright (C) 2007 Tero Marttila
       
     3 # http://marttila.de/~terom/degal/
       
     4 #
       
     5 # This program is free software; you can redistribute it and/or modify
       
     6 # it under the terms of the GNU General Public License as published by
       
     7 # the Free Software Foundation; either version 2 of the License, or
       
     8 # (at your option) any later version.
       
     9 #
       
    10 # This program is distributed in the hope that it will be useful,
       
    11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    13 # GNU General Public License for more details.
       
    14 #
       
    15 # You should have received a copy of the GNU General Public License
       
    16 # along with this program; if not, write to the
       
    17 # Free Software Foundation, Inc.,
       
    18 # 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
       
    19 #
       
    20 
       
    21 import os, os.path
       
    22 
       
    23 import settings, image, utils, helpers, log
       
    24 from template import gallery as gallery_tpl
       
    25 from helpers import url_for_page
       
    26 
       
    27 def dirUp (count=1) :
       
    28     """
       
    29         Returns a relative path to the directly count levels above the current one
       
    30     """
       
    31 
       
    32     if not count :
       
    33         return '.'
       
    34 
       
    35     return os.path.join(*(['..']*count))
       
    36     
       
    37 class Folder (object) :
       
    38     def __init__ (self, name='.', parent=None) :
       
    39         # the directory name, no trailing /
       
    40         self.name = unicode(name.rstrip(os.sep))
       
    41 
       
    42         # our parent Folder, or None
       
    43         self.parent = parent
       
    44 
       
    45         # the path to this dir, as a relative path to the root of the image gallery, always starts with .
       
    46         if parent and name :
       
    47             self.path = parent.pathFor(self.name)
       
    48         else :
       
    49             self.path = self.name
       
    50 
       
    51         # the url-path to the index.html file
       
    52         self.html_path = self.path
       
    53         
       
    54         # dict of fname -> Folder
       
    55         self.subdirs = {}
       
    56 
       
    57         # dict of fname -> Image
       
    58         self.images = {}
       
    59         
       
    60         # our human-friendly title
       
    61         self.title = None
       
    62 
       
    63         # our long-winded description
       
    64         self.descr = ''
       
    65 
       
    66         # is this folder non-empty?
       
    67         self.alive = None
       
    68         
       
    69         # self.images.values(), but sorted by filename
       
    70         self.sorted_images = []
       
    71         
       
    72         # the ShortURL key to this dir
       
    73         self.shorturl_code = None
       
    74 
       
    75         # were we filtered out?
       
    76         self.filtered = False
       
    77    
       
    78     def pathFor (self, *fnames) :
       
    79         """
       
    80             Return a root-relative path to the given path inside this dir
       
    81         """
       
    82         return os.path.join(self.path, *fnames)
       
    83 
       
    84     def index (self, filters=None) :
       
    85         """
       
    86             Look for other dirs and images inside this dir. Filters must be either None,
       
    87             whereupon all files will be included, or a dict of {filename -> next_filter}.
       
    88             If given, only filenames that are present in the dict will be indexed, and in
       
    89             the case of dirs, the next_filter will be passed on to that Folder's index
       
    90             method.
       
    91         """
       
    92 
       
    93         if filters :
       
    94             self.filtered = True
       
    95         
       
    96         # iterate through listdir
       
    97         for fname in os.listdir(self.path) :
       
    98             # the full filesystem path to it
       
    99             fpath = self.pathFor(fname)
       
   100             
       
   101             # ignore dotfiles
       
   102             if fname.startswith('.') :
       
   103                 log.debug("Skipping dotfile %s", fname)
       
   104                 continue
       
   105             
       
   106             # apply filters
       
   107             if filters :
       
   108                 if fname in filters :
       
   109                     next_filter = filters[fname]
       
   110                 else :
       
   111                     log.debug("Skip `%s' as we have a filter", fname)
       
   112                     continue
       
   113             else :
       
   114                 next_filter = None
       
   115                 
       
   116             # recurse into subdirs, but not thumbs/previews
       
   117             if (os.path.isdir(fpath) 
       
   118                 and (fname not in (settings.THUMB_DIR, settings.PREVIEW_DIR))
       
   119                 and (self.parent or fname not in settings.ROOT_IGNORE)
       
   120             ) :
       
   121                 log.down(fname)
       
   122 
       
   123                 f = Folder(fname, self)
       
   124                 
       
   125                 try :
       
   126                     if f.index(next_filter) :   # recursion
       
   127                         # if a subdir is alive, we are alive as well
       
   128                         self.subdirs[fname] = f
       
   129                         self.alive = True
       
   130                 except Exception, e :
       
   131                     log.warning("skip - %s: %s" % (type(e), e))
       
   132 
       
   133                 log.up()
       
   134 
       
   135             # handle images
       
   136             elif os.path.isfile(fpath) and utils.isImage(fname) :
       
   137                 log.next(fname)
       
   138                 self.images[fname] = image.Image(self, fname)
       
   139 
       
   140             # ignore everything else
       
   141             else :
       
   142                 log.debug("Ignoring file %s", fname)
       
   143         
       
   144         # sort and link the images
       
   145         if self.images :
       
   146             self.alive = True
       
   147 
       
   148             # sort the images
       
   149             fnames = self.images.keys()
       
   150             fnames.sort()
       
   151 
       
   152             prev = None
       
   153 
       
   154             # link
       
   155             for fname in fnames :
       
   156                 img = self.images[fname]
       
   157 
       
   158                 img.prev = prev
       
   159 
       
   160                 if prev :
       
   161                     prev.next = img
       
   162 
       
   163                 prev = img
       
   164                 
       
   165                 # add to the sorted images list
       
   166                 self.sorted_images.append(img)
       
   167                 
       
   168         # figure out our title/ descr. Must be done before our parent dir is rendered (self.title)
       
   169         title_path = self.pathFor(settings.TITLE_FILE)
       
   170         
       
   171         self.title, self.descr = utils.readTitleDescr(title_path)
       
   172         
       
   173         # default title for the root dir
       
   174         if self.title or self.descr :
       
   175             self.alive = True
       
   176             pass # use what was in the title file
       
   177             
       
   178         elif not self.parent :
       
   179             self.title = 'Index'
       
   180 
       
   181         else :
       
   182             self.title = self.name
       
   183         
       
   184         if not self.alive :
       
   185             log.debug("Dir %s isn't alive" % self.path)
       
   186 
       
   187         return self.alive
       
   188 
       
   189     def getObjInfo (self) :
       
   190         """
       
   191             Metadata for shorturls2.db
       
   192         """
       
   193         return 'dir', self.path, ''
       
   194 
       
   195     def breadcrumb (self, forImg=None) :
       
   196         """
       
   197             Returns a [(fname, title)] list of this dir's parent dirs
       
   198         """
       
   199 
       
   200         f = self
       
   201         b = []
       
   202         d = 0
       
   203         
       
   204         while f :
       
   205             # functionality of the slightly-hacked-in variety
       
   206             if f is self and forImg is not None :
       
   207                 url = helpers.url_for_page(self.getPageNumber(forImg))
       
   208             else :
       
   209                 url = dirUp(d)
       
   210                 
       
   211             b.insert(0, (url, f.title))
       
   212 
       
   213             d += 1
       
   214             f = f.parent
       
   215         
       
   216         return b
       
   217         
       
   218     def getPageNumber (self, img) :
       
   219         """
       
   220             Get the page number that the given image is on
       
   221         """
       
   222         
       
   223         return self.sorted_images.index(img) // settings.IMAGE_COUNT
       
   224 
       
   225     def countParents (self, acc=0) :
       
   226         if self.parent :
       
   227             return self.parent.countParents(acc+1)
       
   228         else :
       
   229             return acc
       
   230     
       
   231     def inRoot (self, *fnames) :
       
   232         """
       
   233             Return a relative URL from this dir to the given path in the root dir
       
   234         """
       
   235 
       
   236         c = self.countParents()
       
   237 
       
   238         return utils.url_join(*((['..']*c) + list(fnames)))
       
   239 
       
   240     def render (self) :
       
   241         """
       
   242             Render the index.html, Images, and recurse into subdirs
       
   243         """
       
   244         
       
   245         # ded folders are skipped
       
   246         if not self.alive :
       
   247             # dead, skip, no output
       
   248             return
       
   249         
       
   250         index_mtime = utils.mtime(self.pathFor("index.html"))
       
   251         dir_mtime = utils.mtime(self.path)
       
   252 
       
   253         # 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
       
   254         if self.filtered :
       
   255             log.warning("Dir `%s' contents were filtered, so we won't render the gallery index again", self.path)
       
   256 
       
   257         elif index_mtime > dir_mtime :
       
   258             # no changes, pass, ignored
       
   259             pass
       
   260 
       
   261         else :  
       
   262             # create the thumb/preview dirs if needed
       
   263             for dir in (settings.THUMB_DIR, settings.PREVIEW_DIR) :
       
   264                 path = self.pathFor(dir)
       
   265 
       
   266                 if not os.path.isdir(path) :
       
   267                     log.info("mkdir %s", dir)
       
   268                     os.mkdir(path)
       
   269 
       
   270             # sort the subdirs
       
   271             subdirs = self.subdirs.values()
       
   272             subdirs.sort(key=lambda d: d.name)
       
   273             
       
   274             # paginate!
       
   275             images = self.sorted_images
       
   276             image_count = len(images)
       
   277             pages = []
       
   278             
       
   279             while images :
       
   280                 pages.append(images[:settings.IMAGE_COUNT])
       
   281                 images = images[settings.IMAGE_COUNT:]
       
   282 
       
   283             pagination_required = len(pages) > 1
       
   284 
       
   285             if pagination_required :
       
   286                 log.info("%d pages @ %d images", len(pages), settings.IMAGE_COUNT)
       
   287             elif not pages :
       
   288                 log.info("no images, render for subdirs")
       
   289                 pages = [[]]
       
   290 
       
   291             for cur_page, images in enumerate(pages) :
       
   292                 if pagination_required and cur_page > 0 :
       
   293                     shorturl = "%s/%s" % (self.shorturl_code, cur_page+1)
       
   294                 else :
       
   295                     shorturl = self.shorturl_code
       
   296                 
       
   297                 # render to index.html
       
   298                 gallery_tpl.render_to(self.pathFor(url_for_page(cur_page)), 
       
   299                     stylesheet_url               = self.inRoot('style.css'),
       
   300                     title                        = self.title,
       
   301                     breadcrumb                   = self.breadcrumb(),
       
   302                     
       
   303                     dirs                         = subdirs,
       
   304                     images                       = images,
       
   305                     
       
   306                     num_pages                    = len(pages),
       
   307                     cur_page                     = cur_page,
       
   308                     
       
   309                     description                  = self.descr,
       
   310                     
       
   311                     shorturl                     = self.inRoot('s', shorturl),
       
   312                     shorturl_code                = shorturl,
       
   313                 )
       
   314 
       
   315         # render images
       
   316         image_count = len(self.sorted_images)
       
   317         for i, img in enumerate(self.images.itervalues()) :
       
   318             log.next("[%-4d/%4d] %s", i + 1, image_count, img.name)
       
   319 
       
   320             img.render()
       
   321         
       
   322         # recurse into subdirs
       
   323         for dir in self.subdirs.itervalues() :
       
   324             log.down(dir.name)
       
   325 
       
   326             dir.render()
       
   327 
       
   328             log.up()
       
   329