rework html a bit to move the render_* funcs to IRenderable, add XHTMLDocument (ugh), missing flatten
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()