a rather silly shelve-based tagging thing, commiting before I scrap the code and start over with SQLite
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/de-cgi-bin/tags.py Wed Jan 16 16:28:00 2008 +0000
@@ -0,0 +1,93 @@
+#!/usr/bin/env python2.4
+#
+# 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 cgi
+
+import inc
+from lib import tags, template, req, shorturl, utils, settings
+
+tag_list = [tag for tag in req.get_str("tags", "").split("/") if tag]
+
+tag_db = tags.TagDB()
+shorturl_db = shorturl.DB()
+
+root = len(tag_list) + 1
+
+class Tag (object) :
+ def __init__ (self, name, count) :
+ self.title = "%s (%d)" % (name, count)
+ self.name = utils.url(name, trailing=True)
+
+class TagView (object) :
+ def __init__ (self, tag_list) :
+ img_codes = tag_db.imgs(tag_list)
+ more_tags = tag_db.tags(tag_list)
+
+ self.images = []
+
+ for index, key in enumerate(img_codes) :
+ dir, fname = shorturl_db.image_info(key)
+
+ img = Image(key, dir, fname, index)
+ self.images.append(img)
+
+ self.tags = [Tag(name, count) for (name, count) in more_tags]
+
+ def render (self) :
+ descr = ""
+ return template.gallery.render(
+ stylesheet_url = utils.url("style.css", up=root),
+
+ breadcrumb = [(utils.url(up=root), "Index"), (utils.url(up=root-1), "Tags")] + [(utils.url(up=root-(n+1), trailing=True), tag) for n, tag in enumerate(tag_list)],
+
+ title = "Tags :: %s" % (" » ".join(tag_list) or "All"),
+
+ num_pages = 1,
+ cur_page = 0,
+
+ dirs = self.tags,
+ images = self.images,
+
+ description = descr,
+
+ shorturl = None,
+ shorturl_code = None,
+ )
+
+class Image (object) :
+ def __init__ (self, key, dir, fname, index) :
+ self.fname = fname
+ self.name = utils.url_join(dir, fname, abs=True)
+ self.html_name = utils.url_join(dir, fname + ".html", abs=True)
+ self.thumb_name = utils.url_join(dir, settings.THUMB_DIR, fname, abs=True)
+ self.preview_name = utils.url_join(dir, settings.PREVIEW_DIR, fname, abs=True)
+
+ self.shorturl = key
+
+ self.prev = self.next = None
+
+tagview = TagView(tag_list)
+
+print "Content-Type: text/html"
+print
+print tagview.render()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/req.py Wed Jan 16 16:28:00 2008 +0000
@@ -0,0 +1,50 @@
+# 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 cgi
+import Cookie
+import os
+
+vars = cgi.FieldStorage()
+
+# the cookie with the user's current series
+cookie = Cookie.SimpleCookie(os.environ.get('HTTP_COOKIE', None))
+
+class token (object) :
+ pass
+
+REQUIRED_PARAM = token()
+
+def get_str (key, default=REQUIRED_PARAM) :
+ if key in vars :
+ return vars[key].value
+ elif default is REQUIRED_PARAM :
+ raise ValueError("Required param %s" % key)
+ else :
+ return default
+
+def get_int (key, default=REQUIRED_PARAM) :
+ if key in vars :
+ return int(vars[key].value)
+ elif default is REQUIRED_PARAM :
+ raise ValueError("Required param %s" % key)
+ else :
+ return default
+
--- a/lib/shorturl.py Wed Jan 16 14:58:03 2008 +0000
+++ b/lib/shorturl.py Wed Jan 16 16:28:00 2008 +0000
@@ -124,4 +124,22 @@
raise ValueError("%s is not an img" % key)
return dirpath, fname
+
+ def shorturls_for (self, paths) :
+ ret = []
+ for key in self.db.keys() :
+ if key.startswith('_') :
+ continue
+
+ type, dir, fname = self.db[key]
+ path = os.path.join(dir.lstrip('.').lstrip('/'), fname)
+ if path in paths :
+ ret.append(key)
+ paths.remove(path)
+
+ if paths :
+ raise ValueError("Paths not found: %s" % " ".join(paths))
+
+ return ret
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/tags.py Wed Jan 16 16:28:00 2008 +0000
@@ -0,0 +1,89 @@
+import shelve
+
+class TagDB (object) :
+ _imgs_cache = {}
+
+ def __init__ (self, read_only=True) :
+ self.img_tags = shelve.open("img_tags", read_only and 'r' or 'c')
+ self.tag_imgs = shelve.open("tag_imgs", read_only and 'r' or 'c')
+
+ def tag (self, img, tag) :
+ """
+ Associate the given image with the given tag
+ """
+
+ if img not in self.img_tags :
+ self.img_tags[img] = set([tag])
+ else :
+ s = self.img_tags[img]
+ s.add(tag)
+ self.img_tags[img] = s
+
+
+ if tag not in self.tag_imgs :
+ self.tag_imgs[tag] = set([img])
+ else :
+ s = self.tag_imgs[tag]
+ s.add(img)
+ self.tag_imgs[tag] = s
+
+ print "%s <-> %s" % (img, tag)
+
+ def imgs (self, tags) :
+ """
+ Get the set of images that have the given set of tags
+ """
+
+ cache_key = "/".join(tags)
+
+ if cache_key in self._imgs_cache :
+ return self._imgs_cache[cache_key]
+
+ if not tags :
+ return set(self.img_tags.keys())
+
+ img_sets = [self.tag_imgs[tag] for tag in tags]
+
+ res = None
+
+ for img_set in img_sets :
+ if res :
+ res = res & img_set
+ else :
+ res = img_set
+
+ self._imgs_cache[cache_key] = res
+
+ return res
+
+ def tags (self, tags) :
+ """
+ Get the set of tags that are present in the set of images specified by these tags, sorted by count
+
+ This is currently implemented quite inefficiently... giev SQL db?
+ """
+
+ imgs = self.imgs(tags)
+
+ ret = []
+
+ for tag in self.tag_imgs.keys() :
+ if tag in tags :
+ continue
+
+ count = len(self.tag_imgs[tag] & imgs)
+
+ if count :
+ ret.append((tag, count))
+
+ def my_cmp ((at, ac), (bt, bc)) :
+ return cmp((ac, at), (bc, bt))
+
+ ret.sort(reverse=True)
+
+ return ret
+
+ def close (self) :
+ self.img_tags.close()
+ self.tag_imgs.close()
+
--- a/lib/utils.py Wed Jan 16 14:58:03 2008 +0000
+++ b/lib/utils.py Wed Jan 16 16:28:00 2008 +0000
@@ -47,8 +47,9 @@
def url (*parts, **kwargs) :
abs = kwargs.pop('abs', False)
up = kwargs.pop('up', 0)
+ trailing = kwargs.pop('trailing', False)
- return '/'.join(([""]*int(abs)) + ([".."]*up) + list(parts))
+ return '/'.join(([""]*int(abs)) + ([".."]*up) + list(parts) + ([""]*int(trailing)))
url_join = url