diff options
32 files changed, 718 insertions, 1020 deletions
@@ -4,6 +4,7 @@ templates/*.py.bak .*.swp *.db /localconfig.py +/localsettings.py /cache/ pts-index.json.gz profile.out @@ -1,5 +1,18 @@ welcome to the TODO file. +== django-rewrite TODO == + + - this is still a work in progress + - breadcrumbs on django implemented templates + - throw an error if the diff.gz isn't available + - when only one package matches at the package/foo$ url, redirect to pkg/vers + - Last-Modified header would be good + - catch notfound exceptions in gen-patch-info + - handle pages where the argument isn't passed in the url (i.e. email/$) + - Server 500 page + - jump command + - finish django impl with gen-patch-info + == short-range TODO == better handling of more exotic patch systems diff --git a/templates/__init__.py b/__init__.py index e69de29..e69de29 100644 --- a/templates/__init__.py +++ b/__init__.py diff --git a/gen-patch-info.py b/gen-patch-info.py index 3b5847b..17b2a2a 100755 --- a/gen-patch-info.py +++ b/gen-patch-info.py @@ -8,14 +8,14 @@ import gzip import errno import simplejson +os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' +import patchtracker.models + import patchtracker.Conf as Conf -from patchtracker.SourceArchive import Archive, SourcePackage -from patchtracker.DB import PatchTrackerDB +from patchtracker.SourceArchive import Archive from patchtracker.PtsIndex import PtsIndexFile if __name__ == '__main__': - db = PatchTrackerDB() - os.system("cheetah compile templates/skeleton") opts,args = getopt.getopt(sys.argv[1:], "ais:p:") suites = None packages = None @@ -43,15 +43,15 @@ if __name__ == '__main__': print a for s in a.suites(filter=suites): print "suite: ",s - db.saveSuite(s) + suite = patchtracker.models.RepositorySuite.objects.get(name=s) for c in a.components(s): print "\tcomponent:",c - db.saveComponent(c) + component = patchtracker.models.RepositoryComponent.objects.get(name=c) for p in a.sourcepackages(s, c, filter=packages): print "\t\tpackage:",p - db.saveSourcePackage(p) - db.relateSourcePackage(name=p.name, version=p.version, suite=s, - component=c) + m = patchtracker.models.SourcePackageMapping.objects.get(suite=suite, component=component, package__name=p.name) + m.package = p + m.save() if gen_pts_index: pts_idx.add(p) diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..bcdd55e --- /dev/null +++ b/manage.py @@ -0,0 +1,11 @@ +#!/usr/bin/python +from django.core.management import execute_manager +try: + import settings # Assumed to be in the same directory. +except ImportError: + import sys + sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) + sys.exit(1) + +if __name__ == "__main__": + execute_manager(settings) diff --git a/patchtracker/CacheObject.py b/patchtracker/CacheObject.py deleted file mode 100644 index c37c3b5..0000000 --- a/patchtracker/CacheObject.py +++ /dev/null @@ -1,63 +0,0 @@ -import errno -import gzip -import md5 -import os - -import Conf - -class CacheMissException (Exception): - pass - -class CacheObject: - """ A CacheObject is a compressed on-disk version of a serialized object. - """ - def __init__ (self, key=None, keys=None): - self.obj = None - #print "CacheObject: key=%s keys=%s"%(key, keys) - if not key and not keys: - raise "CacheObject needs at least one of key, keys" - checksum = md5.md5() - if key: - checksum.update(key) - if keys: - for k in keys: - checksum.update(str(k)) - self.path = os.path.sep.join([Conf.cachedir, checksum.hexdigest()]) - - def get(self): - if not self.obj: - try: - if Conf.cachecompress: - self.obj = gzip.GzipFile(self.path).read() - else: - self.obj = file(self.path).read() - except IOError, e: - if e.errno != errno.ENOENT: - raise e - else: - #print "CacheObject: cache miss" - raise CacheMissException("Object not present in cache") - #print "CacheObject: cache hit" - return self.obj - - def put(self, obj): - self.obj = obj - #print "CacheObject: cache put" - try: - if Conf.cachecompress: - gzip.GzipFile(self.path, "wb").write(str(self.obj)) - else: - file(self.path, "wb").write(str(self.obj)) - except Exception, e: - os.unlink(self.path) - raise e - -if __name__ == '__main__': - co = CacheObject( key="magic" ) - try: - print "going to try to read an object before it exists" - print "first line:", co.get().split()[0] - except CacheMissException: - print "file was missing as expected. now let's try to put it and fetch it" - co.put(file("/etc/passwd").read()) - print "first line:", co.get().split()[0] diff --git a/patchtracker/ComplexQueries.py b/patchtracker/ComplexQueries.py new file mode 100644 index 0000000..be7e30f --- /dev/null +++ b/patchtracker/ComplexQueries.py @@ -0,0 +1,29 @@ +import models + +class PackageIndexQuery: + def __init__( self, queryobj ): + self.dists = [d.name for d in models.RepositorySuite.objects.all()] + self.index = {} + self.packages = [] + self.queryobj = queryobj + + for mapping in queryobj: + self._addPackageMapping(mapping.package, mapping.suite) + + def _addPackageMapping( self, package, suite ): + if package.name not in self.index: + self.index[package.name] = [None] * len(self.dists) + self.packages.append(package) + self.index[package.name][self.dists.index(suite.name)] = package + + def __iter__( self ): + for pkg in self.packages: + yield { 'package':pkg, 'entries':self.index[pkg.name] } + +class PackageIndex (PackageIndexQuery): + def __init__( self, index ): + mappings = models.SourcePackageMapping.objects.order_by('package__name').filter( package__name=index ) + if not mappings: + mappings = models.SourcePackageMapping.objects.order_by('package__name').filter( package__name__startswith=index ) + + PackageIndexQuery.__init__(self, mappings) diff --git a/patchtracker/DB.py b/patchtracker/DB.py deleted file mode 100644 index 0bb0966..0000000 --- a/patchtracker/DB.py +++ /dev/null @@ -1,193 +0,0 @@ -from pysqlite2 import dbapi2 as sqlite -import Conf -import os -import errno - -import patchtracker.SourceArchive as SourceArchive - -def srcpkg_factory(cursor, row): - d = {} - for idx, col in enumerate(cursor.description): - d[col[0]] = row[idx] - - info = {} - info['Files'] = [] - if d['diffgz_name']: - diffgz = {'name':d['diffgz_name'], 'size':d['diffgz_size'], - 'md5sum':d['diffgz_md5sum'] } - info['Files'].append(diffgz) - if d['debtar_name']: - debtar = {'name':d['debtar_name'], 'size':d['debtar_size'], - 'md5sum':d['debtar_md5sum'] } - info['Files'].append(debtar) - - colmap = {'name':'Package','version':'Version','format':'Format', - 'loc':'Directory','maintainer':'Maintainer','uploaders':'Uploaders'} - - for col,field in colmap.iteritems(): - info[field] = d[col] - return SourceArchive.SourcePackage(info) - -def srcpkg_collection_factory(cursor, row): - d = {} - rest = {} - for idx, col in enumerate(cursor.description): - if not d.has_key(col[0]): - d[col[0]] = row[idx] - else: - rest[col[0]] = row[idx] - - info = {} - info['Files'] = [] - if d['diffgz_name']: - diffgz = {'name':d['diffgz_name'], 'size':d['diffgz_size'], - 'md5sum':d['diffgz_md5sum'] } - info['Files'].append(diffgz) - if d['debtar_name']: - debtar = {'name':d['debtar_name'], 'size':d['debtar_size'], - 'md5sum':d['debtar_md5sum'] } - info['Files'].append(debtar) - - colmap = {'name':'Package','version':'Version','format':'Format', - 'loc':'Directory','maintainer':'Maintainer','uploaders':'Uploaders'} - - for col,field in colmap.iteritems(): - info[field] = d[col] - return (SourceArchive.SourcePackage(info), rest) - - -class PatchTrackerDB: - def __init__(self, dbname=Conf.database): - self.db = sqlite.connect(dbname) - cursor = self.db.cursor() - cursor.execute("SELECT * FROM sqlite_master WHERE name='packages'") - if not cursor.fetchone(): - print "repopulating empty database..." - nextcmd = "" - for l in file(Conf.sqlschema).readlines(): - if len(l.strip()): - nextcmd += " " + l.strip() - else: - cursor.execute(nextcmd) - nextcmd = "" - - def setFactory(self, factory): - self.db.row_factory = factory - - def saveSourcePackage(self, srcpkg): - cursor = self.db.cursor() - #print "creating new record for",srcpkg - q = "INSERT OR IGNORE INTO packages \ - (name,format,loc,version,diffgz_name,diffgz_size,diffgz_md5sum,debtar_name,debtar_size,debtar_md5sum,maintainer,uploaders) \ - VALUES (?,?,?,?,?,?,?,?,?,?,?,?)" - cursor.execute(q, (srcpkg.name,srcpkg.format,srcpkg.loc, - srcpkg.version,srcpkg.diffgz_name,srcpkg.diffgz_size, - srcpkg.diffgz_md5sum, - srcpkg.debtar_name,srcpkg.debtar_size, - srcpkg.debtar_md5sum, - srcpkg.maintainers,srcpkg.uploaders)) - - def saveSuite(self, suite): - q = "INSERT INTO suites (name) VALUES (?)" - cursor = self.db.cursor() - cursor.execute(q, (suite,)) - - def saveComponent(self, component): - q = "INSERT INTO components (name) VALUES (?)" - cursor = self.db.cursor() - cursor.execute(q, (component,)) - - def findCollection(self, package="%", version=None, email=None): - oldfactory = self.db.row_factory - self.db.row_factory = srcpkg_collection_factory - cursor = self.db.cursor() - toc = SourceArchive.SourcePackageIndex() - q = "SELECT * FROM packages AS p,package_rel_map AS m,suites AS s \ - WHERE p.name LIKE ? AND p.id = m.package_id AND m.suite_id = s.id" - qargs = (package,) - if version: - q += " AND p.version = ?" - qargs += (version,) - if email: - q += " AND ( p.maintainer like ? OR p.uploaders like ? )" - qargs += ("%%%s%%"%(email),"%%%s%%"%(email)) - - cursor.execute(q, qargs) - # use srcpkg_factory to fetch sourcepackages, once per suite - for srcpkg,rest in cursor.fetchall(): - toc.ins(srcpkg, rest["name"]) - self.db.row_factory = oldfactory - return toc - - def findLetterToc(self, letter): - return self.findCollection(package=letter+"%").getletter(letter) - - def findIndices(self): - indices = [] - cursor = self.db.cursor() - q1 = "SELECT DISTINCT SUBSTR(name,1,1) FROM packages \ - WHERE name NOT LIKE 'lib%'" - q2 = "SELECT DISTINCT SUBSTR(name,1,4) FROM packages \ - WHERE name LIKE 'lib%'" - - for q in [q1, q2]: - cursor.execute(q) - for idx in cursor.fetchall(): - indices.append(idx[0]) - - indices.sort() - return indices - - def relateSourcePackage(self, name, version, suite, component): - q = "INSERT INTO package_rel_map \ - (package_id,suite_id,component_id,marked) \ - VALUES ((SELECT id FROM packages WHERE name=? AND version=?), \ - (SELECT id FROM suites WHERE name=?), \ - (SELECT id FROM components WHERE name=?), 1)" - cursor = self.db.cursor() - cursor.execute(q, (name, version, suite, component)) - - def findDiffGz(self, pkgname, version): - q = "SELECT diffgz_name,loc FROM packages WHERE name=? AND version=?" - cursor = self.db.cursor() - cursor.execute(q, (pkgname, version)) - try: - diffgz,loc = cursor.fetchone() - return os.sep.join([Conf.archive_root, loc, diffgz]) - except: - return None - - def findDebTar(self, pkgname, version): - q = "SELECT debtar_name,loc FROM packages WHERE name=? AND version=?" - cursor = self.db.cursor() - cursor.execute(q, (pkgname, version)) - try: - debtar,loc = cursor.fetchone() - return os.sep.join([Conf.archive_root, loc, debtar]) - except: - return None - - def prune(self): - q = "DELETE FROM package_rel_map WHERE marked != 1" - cursor = self.db.cursor() - cursor.execute(q) - - def unmark(self): - q = "UPDATE package_rel_map SET marked=0" - cursor = self.db.cursor() - cursor.execute(q) - - def finalize(self): - self.db.commit() - - def __del__(self): - self.finalize() - - -if __name__ == "__main__": - print "patch tracker db testing" - d = PatchTrackerDB(dbname="foo.db") - sp = SourceArchive.SourcePackage({'Package':'foo','Format':'blah','Directory':'/foo','Version':'1.2.3.4','Files':[]}) - d.saveSourcePackage(sp) - d.finalize() - diff --git a/patchtracker/DebTarHandler.py b/patchtracker/DebTarHandler.py deleted file mode 100644 index 311e39b..0000000 --- a/patchtracker/DebTarHandler.py +++ /dev/null @@ -1,27 +0,0 @@ -import os -import stat -import sys - -import Patch - -class DebTarHandler: - diff = None - def __init__(self,fname): - self.tarfile = fname - self.size = os.stat(fname)[stat.ST_SIZE] - - def series(self): - return Patch.Quilt30PatchSeries(self.tarfile) - -if __name__ == "__main__": - print "DebTarHandler testing" - try: - dh = DebTarHandler(sys.argv[1]) - except IndexError: - print "usage: %s <diffgz>"%(sys.argv[0]) - sys.exit(1) - - print "series:" - for f,p in dh.series(): - print f - print p diff --git a/patchtracker/DiffGzHandler.py b/patchtracker/DiffGzHandler.py deleted file mode 100644 index 84be827..0000000 --- a/patchtracker/DiffGzHandler.py +++ /dev/null @@ -1,74 +0,0 @@ -import tempfile -import sys -import os -import stat - -from Patch import Patch, PatchSeries - -class DiffGzException(Exception): - pass - -class DiffGzHandler: - diff = None - def __init__(self,fname): - self.diff = fname - self.size = os.stat(fname)[stat.ST_SIZE] - - def filterdiff(self, include=None, exclude=None): - cmd = ["filterdiff","-z","-p","1"] - if include: - cmd += [ "-i", include] - elif exclude: - cmd += [ "-x", exclude] - else: - raise Exception("DiffGzHandler.filterdiff called w/o include/exclude") - i,o,e=os.popen3(cmd+[self.diff]) - i.close() - p = Patch(o) - err = e.read() - if len(err): - raise DiffGzException("filterdiff gave errors: "+err) - return p - - def debiandir(self): - return self.filterdiff(include='debian/*') - - def nondebiandir(self): - return self.filterdiff(exclude='debian/*') - - def series(self): - patches = None - embedded = self.filterdiff(include='debian/patches*') - - # XXX *cough* cache *cough* - if embedded.lines(): - td = tempfile.mkdtemp() - i,o,e=os.popen3("patch -d %s -p3"%(td)) - i.write(str(embedded)) - i.close() - err = e.read() - if len(err): - raise Exception("unable to extract series patches:\n"+err) - patches = PatchSeries(td) - os.system("rm -rf %s"%(td)) - - return patches - -if __name__ == "__main__": - print "DiffGzHandler testing" - try: - dh = DiffGzHandler(sys.argv[1]) - except IndexError: - print "usage: %s <diffgz>"%(sys.argv[0]) - sys.exit(1) - - print "debian dir:" - print dh.debiandir().diffstat() - print "nondebian dir:" - print dh.nondebiandir().diffstat() - print "series:" - s = dh.series() - print s - for name,patch in s.iterpatches(): - print "patch:",name - print patch.diffstat() diff --git a/patchtracker/Patch.py b/patchtracker/Patch.py index 6533910..c872d1c 100644 --- a/patchtracker/Patch.py +++ b/patchtracker/Patch.py @@ -1,7 +1,12 @@ -import sys -import os import errno +import os +import stat +import sys +import tempfile from glob import glob +import pygments +import pygments.lexers +import pygments.formatters import tarfile class Diffstat: @@ -39,6 +44,14 @@ class Patch: def diffstat(self): return Diffstat(self) + def highlight(self): + for enc in ['utf-8', 'latin-1']: + try: + return pygments.highlight(str(self).decode(enc), pygments.lexers.DiffLexer(), + pygments.formatters.HtmlFormatter(style='colorful', noclasses=True, encoding=enc, nobackground=True)) + except UnicodeDecodeError: + pass + class GenericPatchSeries (list): def blank(self): self.names = [] @@ -164,6 +177,64 @@ class Quilt30PatchSeries (GenericPatchSeries): for name in self.names: self.patches[name] = Patch(self.tarfh.extractfile("debian/patches/"+name)) +class DebTarHandler: + diff = None + def __init__(self,fname): + self.tarfile = fname + self.size = os.stat(fname)[stat.ST_SIZE] + + def series(self): + return Quilt30PatchSeries(self.tarfile) + +class DiffGzException(Exception): + pass + +class DiffGzHandler: + diff = None + def __init__(self,fname): + self.diff = fname + self.size = os.stat(fname)[stat.ST_SIZE] + + def filterdiff(self, include=None, exclude=None): + cmd = ["filterdiff","-z","-p","1"] + if include: + cmd += [ "-i", include] + elif exclude: + cmd += [ "-x", exclude] + else: + raise Exception("DiffGzHandler.filterdiff called w/o include/exclude") + i,o,e=os.popen3(cmd+[self.diff]) + i.close() + p = Patch(o) + err = e.read() + if len(err): + raise DiffGzException("filterdiff gave errors: "+err) + return p + + def debiandir(self): + return self.filterdiff(include='debian/*') + + def nondebiandir(self): + return self.filterdiff(exclude='debian/*') + + def series(self): + patches = None + embedded = self.filterdiff(include='debian/patches*') + + # XXX *cough* cache *cough* + if embedded.lines(): + td = tempfile.mkdtemp() + i,o,e=os.popen3("patch -d %s -p3"%(td)) + i.write(str(embedded)) + i.close() + err = e.read() + if len(err): + raise Exception("unable to extract series patches:\n"+err) + patches = PatchSeries(td) + os.system("rm -rf %s"%(td)) + + return patches + if __name__ == "__main__": print "Patch.py testing" try: diff --git a/patchtracker/PtsIndex.py b/patchtracker/PtsIndex.py index 08a6e68..831b4b6 100644 --- a/patchtracker/PtsIndex.py +++ b/patchtracker/PtsIndex.py @@ -1,7 +1,7 @@ import os import Conf -from DiffGzHandler import DiffGzHandler +import Patch class PtsIndexPackageInfo ( dict ): def __init__ (self, srcpkg): @@ -11,7 +11,7 @@ class PtsIndexPackageInfo ( dict ): if srcpkg.diffgz_name: diffgz = os.sep.join([Conf.archive_root,srcpkg.loc,srcpkg.diffgz_name]) - dh = DiffGzHandler( diffgz ) + dh = Patch.DiffGzHandler( diffgz ) ser = dh.series() if ser: self['series-patches'] = len(ser) diff --git a/patchtracker/ReqHandler.py b/patchtracker/ReqHandler.py deleted file mode 100755 index 90fc75a..0000000 --- a/patchtracker/ReqHandler.py +++ /dev/null @@ -1,222 +0,0 @@ -# -*- coding: utf-8 -*- - -import cgi -import os -import sys - -import patchtracker.Conf as Conf -from patchtracker.Templates import ErrorTemplate, PatchTemplate, PackageVersTemplate, LetterTocTemplate, FrontPageTemplate, SearchResultsTemplate -from patchtracker.DiffGzHandler import DiffGzHandler, DiffGzException -from patchtracker.DebTarHandler import DebTarHandler -from patchtracker.CacheObject import CacheObject, CacheMissException -import patchtracker.DB as DB -from patchtracker.DB import PatchTrackerDB -import pygments -from pygments.lexers import DiffLexer -from pygments.formatters import HtmlFormatter -import patchtracker.SourceArchive as SourceArchive - -class ReqHandlerException(Exception): - def __init__(self, msg, code="500 Oh noes"): - Exception.__init__(self, msg) - self.status=code - -class Cmd: - def __init__(self): - self.content_type = 'text/html' - self.status = "200 OK" - -class ErrorCmd(Cmd): - def __init__(self, msg, code="500 Oh noes"): - Cmd.__init__(self) - self.status = code - self.msg = msg - - def output(self): - return str(ErrorTemplate(self.msg)) - -class PatchCmd(Cmd): - def __init__(self, args): - Cmd.__init__(self) - self.db = PatchTrackerDB() - self.patchtype,mode,pkgname,version = args[0:4] - self.parsemode(mode) - dh = self.make_diffhandler(pkgname,version) - if self.patchtype == "series": - self.patchname = os.sep.join(args[4:]) - self.content = dh.series().fetch(self.patchname) - elif self.patchtype == "debianonly": - self.patchname = "debian-dir only changes" - self.content = dh.debiandir() - elif self.patchtype == "nondebian": - self.patchname = "direct (non packaging) changes" - self.content = dh.nondebiandir() - elif self.patchtype == "misc": - self.patchname = os.sep.join(args[4:]) - self.content = dh.filterdiff(include=self.patchname) - else: - raise ReqHandlerException("unhandled patch type '%s'"%(self.patchtype)) - self.pkgname = pkgname - self.version = version - - def parsemode(self, mode): - if mode == "view" or mode == "dl": - self.mode = mode - else: - raise ReqHandlerException("unhandled display mode '%s'"%(mode)) - if mode == "dl": - self.content_type = "text/x-diff" - - # XXX this is kinda ugly... - def make_diffhandler(self, pkgname, vers): - dfile = self.db.findDiffGz(pkgname,vers) - if dfile: - return DiffGzHandler(dfile) - else: - dfile = self.db.findDebTar(pkgname, vers) - if dfile: - return DebTarHandler(dfile) - else: - raise ReqHandlerException("can not find diff file for %s / %s"%(pkgname,vers)) - - def output(self): - if self.mode == "dl": - return str(self.content) - else: - return str(PatchTemplate(pkg=self.pkgname,vers=self.version, - patch=self.content,name=self.patchname, - patchtype=self.patchtype)) - -class PackageCmd(Cmd): - def __init__(self, args): - Cmd.__init__(self) - db = PatchTrackerDB() - self.name = args[0] - if len(args) > 1: - version = args[1] - else: - version = None - self.toc = db.findCollection(package=self.name, version=version) - - # if there's no match, try with a wildcard match - if not self.toc.size(): - # ... but don't allow pathologically short names - if len(self.name) < 3: - raise ReqHandlerException("search terms must be 3 or more letters...") - else: - self.toc = db.findCollection(package="%"+self.name+"%", version=version) - - plist = self.toc.getletter(self.name) - if not plist or len(plist) == 0: - raise ReqHandlerException("can't find any package named or containing '%s'"%self.name, code="404 ENOPKG kthxbye") - - def output(self): - p = self.toc.getpackage(self.name) - # if there is no match, or if multiple versions were returned - if not p or len(set(map(lambda x: x.version, p.values()))) > 1: - querydesc = "package name contains" - return str(SearchResultsTemplate(self.name, querydesc, self.toc)) - else: - return str(PackageVersTemplate(p.popitem()[1])) - -class IndexCmd(Cmd): - def __init__(self, args): - Cmd.__init__(self) - if len(args) < 1 or not len(args[0]): - raise ReqHandlerException("please provide a letter on which to index") - else: - self.db = PatchTrackerDB() - self.letter = args[0] - self.toc = self.db.findLetterToc(self.letter) - - def output(self): - return str(LetterTocTemplate(self.letter, self.toc)) - -class MaintCmd(Cmd): - def __init__(self, args): - Cmd.__init__(self) - if len(args) < 1 or not len(args[0]): - raise ReqHandlerException("please provide a email address on which to index") - else: - self.db = PatchTrackerDB() - self.email = args[0] - self.toc = self.db.findCollection(email=self.email) - - def output(self): - return str(SearchResultsTemplate(self.email, "maintainer email", self.toc)) - -class JumpCmd(Cmd): - def __init__(self, env): - Cmd.__init__(self) - form = cgi.FieldStorage(fp=env['wsgi.input'],environ=env) - self.name = form.getfirst("package") - self.uri = "%s/package/%s"%(Conf.root_url, self.name) - self.status = "302 Try this other place kthx" - - def output(self): - return "" - -class FrontPageCmd(Cmd): - def __init__(self): - Cmd.__init__(self) - self.db = PatchTrackerDB() - self.index = self.db.findIndices() - - def output(self): - return str(FrontPageTemplate(self.index)) - -class CmdHandler: - def __init__(self, env): - self.headers = [] - uri = Conf.root_url+env['PATH_INFO'] - self.cacheobj = None - #print "Accept:",env['HTTP_ACCEPT'] - - args = uri[len(Conf.root_url)+1:].split("/") - cmdarg = args[0] - cacheable = False - if cmdarg == "patch": - self.cmd = PatchCmd(args[1:]) - cacheable = True - elif cmdarg == "package": - self.cmd = PackageCmd(args[1:]) - if len(args[1:]) > 1: - cacheable = True - elif cmdarg == "index": - self.cmd = IndexCmd(args[1:]) - elif cmdarg == "jump": - self.cmd = JumpCmd(env) - self.headers.append( ('Location', self.cmd.uri) ) - elif cmdarg == "email": - self.cmd = MaintCmd(args[1:]) - elif not len(cmdarg): - self.cmd = FrontPageCmd() - else: - self.cmd = ErrorCmd("invalid command/location '%s'"%(cmdarg), "404 Not found") - - if Conf.caching and cacheable: - self.cacheobj = CacheObject(key=uri) - - self.headers.append( ('Content-type', self.cmd.content_type) ) - self.status = self.cmd.status - - def output(self): - result = None - try: - if self.cacheobj: - result = self.cacheobj.get() - else: - result = self.cmd.output() - except CacheMissException: - result = self.cmd.output() - self.cacheobj.put(result) - except DiffGzException, e: - return ErrorCmd(str(e), "500 Oh Noez!!1!").output() - - return result - -if __name__ == '__main__': - fake_env = { 'PATH_INFO': sys.argv[1] } - cmdh = CmdHandler(fake_env) - print "Status: %s\nHeaders: %s"%(cmdh.status, cmdh.headers) - print cmdh.output() diff --git a/patchtracker/SourceArchive.py b/patchtracker/SourceArchive.py index 0cdeb10..ce3459a 100755 --- a/patchtracker/SourceArchive.py +++ b/patchtracker/SourceArchive.py @@ -5,6 +5,8 @@ from gzip import GzipFile from debian_bundle import deb822 import difflib +import models + class Archive: def __init__(self, dir, suitefilter=None, pkgfilter=None): self.root = None @@ -65,7 +67,33 @@ class Archive: if filter and not fdict.has_key(ent['Package']): continue else: - yield SourcePackage(ent) + pkg = models.SourcePackage() + # some other defaults + pkg.name = ent['Package'] + pkg.format = ent['Format'] + pkg.loc = ent['Directory'] + pkg.version = ent['Version'] + try: + pkg.maintainers = unicode(ent['Maintainer']) + except UnicodeDecodeError: + pkg.maintainers = unicode(ent['Maintainer'], 'latin-1') + if ent.has_key('Uploaders'): + try: + pkg.uploaders = unicode(ent['Uploaders']) + except UnicodeDecodeError: + pkg.uploaders = unicode(ent['Uploaders'], 'latin-1') + + for f in ent['Files']: + if fnmatch(f['name'], '*.diff.gz'): + pkg.diffgz_name=f['name'] + pkg.diffgz_size=f['size'] + pkg.diffgz_md5sum=f['md5sum'] + elif fnmatch(f['name'], '*.debian.tar.*'): + pkg.debtar_name=f['name'] + pkg.debtar_size=f['size'] + pkg.debtar_md5sum=f['md5sum'] + + yield pkg def __str__(self): return "Archive rooted at "+self.root @@ -89,55 +117,6 @@ def getidx(letter): else: return name[0:4] -class SourcePackage: - def __init__(self, info): - # attributes for debian .diff.gz source packages - self.diffgz_name = None - self.diffgz_size = None - self.diffgz_md5sum = None - self.diffgz = None - - # attributes for debian .debian.tar.gz source packages - self.debtar_name = None - self.debtar_size = None - self.debtar_md5sum = None - self.debtar = None - - # some other defaults - self.type = "Native" - self.name = info['Package'] - self.format = info['Format'] - self.loc = info['Directory'] - self.version = info['Version'] - self.uploaders = None - - try: - self.maintainers = unicode(info['Maintainer']) - except UnicodeDecodeError: - self.maintainers = unicode(info['Maintainer'], 'latin-1') - if info.has_key('Uploaders'): - try: - self.uploaders = unicode(info['Uploaders']) - except UnicodeDecodeError: - self.uploaders = unicode(info['Uploaders'], 'latin-1') - - self.idx = getidx(self) - - for f in info['Files']: - if fnmatch(f['name'], '*.diff.gz'): - self.diffgz_name=f['name'] - self.diffgz_size=f['size'] - self.diffgz_md5sum=f['md5sum'] - self.type = "Debian-diff" - elif fnmatch(f['name'], '*.debian.tar.*'): - self.debtar_name=f['name'] - self.debtar_size=f['size'] - self.debtar_md5sum=f['md5sum'] - self.type = "Debian-diff" - - def __str__(self): - return self.name - class SourcePackageIndex: def __init__(self): self.pkgs = {} diff --git a/patchtracker/Templates.py b/patchtracker/Templates.py deleted file mode 100755 index 42140a8..0000000 --- a/patchtracker/Templates.py +++ /dev/null @@ -1,103 +0,0 @@ -from patchtracker import Conf, DB -from patchtracker.DiffGzHandler import DiffGzHandler -from patchtracker.DebTarHandler import DebTarHandler -from patchtracker.SourceArchive import ReleaseList - -from Cheetah.Template import Template -from Cheetah.Compiler import Compiler -import os, errno -import re - -class OurTemplate(Template): - def __init__(self, file, searchList=None): - ourSearchList={"conf":Conf, "crumbs":[]} - if searchList: - for k,v in searchList.iteritems(): - ourSearchList[k]=v - self.escape_name = self._escape_name - self.wrappable_version = self._wrappable_version - self.link = self._link - Template.__init__(self, file=file, searchList=ourSearchList) - - def _link(self, where, name): - return str("<a href=\"%s\">%s</a>"%(where,name)) - - def _escape_name(self, name): - return re.sub("([^a-zA-Z0-9-])", (lambda x: "_%d"%(ord(x.group(1)))), name) - - def _wrappable_version(self, version): - """ output a version that can be broken up by a web browser using - the unicode zero-whitespace-break character (​) """ - return '​'.join([version[i:i+12] for i in range(0,len(version),12)]) - -class PackageVersTemplate(OurTemplate): - def __init__(self, srcpkg): - self.src = srcpkg - tpl=os.sep.join([Conf.template_dir, "package_vers.tmpl"]) - sl = {} - # XXX c/p from PatchCmd.make_diffhandler - db = DB.PatchTrackerDB() - dfile = db.findDiffGz(srcpkg.name,srcpkg.version) - if dfile: - sl['diffhandler'] = DiffGzHandler(dfile) - else: - dfile = db.findDebTar(srcpkg.name, srcpkg.version) - if dfile: - sl['diffhandler'] = DebTarHandler(dfile) - sl['crumbs'] = [("package/"+srcpkg.name,srcpkg.name), - ("package/"+srcpkg.name+"/"+srcpkg.version, - srcpkg.name+"/"+srcpkg.version)] - OurTemplate.__init__(self, file=tpl, searchList=sl) - -class FrontPageTemplate(OurTemplate): - def __init__(self, indices): - tpl = os.sep.join([Conf.template_dir, "frontpage.tmpl"]) - OurTemplate.__init__(self, file=tpl) - self.indices = indices - -class LetterTocTemplate(OurTemplate): - def __init__(self, letter, collection): - self.pkgs = collection - self.idx = letter - self.dists = ReleaseList("debian") - sl = {} - sl['crumbs'] = [("index/"+letter,"index for "+letter)] - tpl = os.sep.join([Conf.template_dir, "letter_toc.tmpl"]) - OurTemplate.__init__(self, file=tpl, searchList=sl) - -class SearchResultsTemplate(OurTemplate): - def __init__(self, search, searchtype, collection): - self.pkgs = {} - self.idx = search - self.searchtype = searchtype - self.dists = ReleaseList("debian") - for idx in collection.indices(): - for name,packagelist in collection.getletter(idx).iteritems(): - self.pkgs[name] = packagelist - tpl = os.sep.join([Conf.template_dir, "searchresults.tmpl"]) - OurTemplate.__init__(self, file=tpl) - - -class ErrorTemplate(OurTemplate): - def __init__(self, msg): - tpl = os.sep.join([Conf.template_dir, "cgi_error.tmpl"]) - OurTemplate.__init__(self, file=tpl, searchList={'error':msg}) - -class PatchTemplate(OurTemplate): - def __init__(self, pkg=None, vers=None, name=None, patch=None,patchtype=None): - tpl = os.sep.join([Conf.template_dir, "patch_view.tmpl"]) - sl = {'package':pkg, 'version':vers, 'name':name, - 'patch':patch, 'patchtype':patchtype} - sl['crumbs'] = [("package/"+pkg,pkg), - ("package/"+pkg+"/"+vers, pkg+"/"+vers)] - if patchtype == "debianonly": - sl['crumbs'].append( ("patch/%s/view/%s/%s"%(patchtype,pkg,vers), - "./debian-dir only patch") ) - elif patchtype == "series": - sl['crumbs'].append(("patch/%s/view/%s/%s/%s"%(patchtype,pkg,vers,name), - "series patch "+name) ) - elif patchtype == "misc": - sl['crumbs'].append(("patch/%s/view/%s/%s/%s"%(patchtype,pkg,vers,name), - "direct patch of "+name) ) - OurTemplate.__init__(self, file=tpl, searchList=sl) - diff --git a/patchtracker/admin.py b/patchtracker/admin.py new file mode 100644 index 0000000..0fbd418 --- /dev/null +++ b/patchtracker/admin.py @@ -0,0 +1,7 @@ +import models +import django.contrib.admin + +django.contrib.admin.site.register(models.SourcePackage) +django.contrib.admin.site.register(models.RepositorySuite) +django.contrib.admin.site.register(models.RepositoryComponent) +django.contrib.admin.site.register(models.SourcePackageMapping) diff --git a/patchtracker/models.py b/patchtracker/models.py new file mode 100644 index 0000000..1758609 --- /dev/null +++ b/patchtracker/models.py @@ -0,0 +1,89 @@ +import os +import re + +import Conf +import Patch + +from django.db import models + +class SourcePackage (models.Model): + name = models.CharField(max_length=1024) + version = models.CharField(max_length=1024) + format = models.CharField(max_length=64) + loc = models.CharField(max_length=4096) + diffgz_name = models.CharField(max_length=4096, null=True, blank=True) + diffgz_size = models.IntegerField(null=True, blank=True) + diffgz_md5sum = models.CharField(max_length=32, null=True, blank=True) + debtar_name = models.CharField(max_length=4096, null=True, blank=True) + debtar_size = models.IntegerField(null=True, blank=True) + debtar_md5sum = models.CharField(max_length=32, null=True, blank=True) + maintainer = models.CharField(max_length=1024) + uploaders = models.CharField(max_length=4096, null=True, blank=True) + + def type( self ): + """ Source package "type". Not incredibly useful, i guess... """ + if self.diffgz_name or self.debtar_name: + return "Debian-diff" + else: + return "Native" + + def diffhandler( self ): + """ Return a diffhandler object too the caller """ + if self.diffgz_name: + dgz = os.sep.join([Conf.archive_root, self.loc, self.diffgz_name]) + return Patch.DiffGzHandler(dgz) + elif self.debtar_name: + dtar = os.sep.join([Conf.archive_root, self.loc, self.debtar_name]) + return Patch.DebTarHandler(dtar) + + def anchor_name(self): + """ output a version of the package name suitable for use as an + anchor name (i.e. <a name="foo">) """ + return re.sub("([^a-zA-Z0-9-])", (lambda x: "_%d"%(ord(x.group(1)))), self.name) + + def wrappable_version(self): + """ output a version that can be broken up by a web browser using + the unicode zero-whitespace-break character (​) """ + return '​'.join([self.version[i:i+12] for i in range(0,len(self.version),12)]) + + def __unicode__( self ): + return "%s (%s)"%(self.name, self.version) + + class Meta: + managed = False + db_table = 'packages' + unique_together = (('name','version'),) + +class RepositorySuite (models.Model): + name = models.CharField(max_length=1024, unique=True) + + def __unicode__( self ): + return unicode(self.name) + + class Meta: + managed = False + db_table = 'suites' + +class RepositoryComponent (models.Model): + name = models.CharField(max_length=1024, unique=True) + + def __unicode__( self ): + return unicode(self.name) + + class Meta: + managed = False + db_table = 'components' + +class SourcePackageMapping (models.Model): + rowid = models.AutoField(primary_key=True) + package = models.ForeignKey(SourcePackage) + suite = models.ForeignKey(RepositorySuite) + component = models.ForeignKey(RepositoryComponent) + + def __unicode__( self ): + return "%s / %s / %s"%(unicode(self.package),unicode(self.suite),unicode(self.component)) + + class Meta: + managed = False + db_table = 'package_rel_map' + unique_together = (('package','suite','component'),) diff --git a/patchtracker/tests.py b/patchtracker/tests.py new file mode 100644 index 0000000..2247054 --- /dev/null +++ b/patchtracker/tests.py @@ -0,0 +1,23 @@ +""" +This file demonstrates two different styles of tests (one doctest and one +unittest). These will both pass when you run "manage.py test". + +Replace these with more appropriate tests for your application. +""" + +from django.test import TestCase + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.failUnlessEqual(1 + 1, 2) + +__test__ = {"doctest": """ +Another way to test that 1 + 1 is equal to 2. + +>>> 1 + 1 == 2 +True +"""} + diff --git a/patchtracker/urls.py b/patchtracker/urls.py new file mode 100644 index 0000000..b174272 --- /dev/null +++ b/patchtracker/urls.py @@ -0,0 +1,18 @@ +from django.conf.urls.defaults import * +import views + +urlpatterns = patterns('', + url(r'^package/(?P<package>\w+)/(?P<version>[^/]+)$', + views.package_vers), + url(r'^package/(?P<index>\w+)$', + views.display_toc), + url(r'^index/(?P<index>[^/]+)$', + views.display_toc), + url(r'^email/(?P<maintainer>.*)$', + views.maintainer_search), + url(r'^patch/(?P<patchType>(debianonly|misc|nondebian|series))/view/(?P<package>\w+)/(?P<version>[^/]+)/(?P<patchName>.*)$', + views.display_patch), + url(r'^patch/(?P<patchType>(debianonly|misc|nondebian|series))/dl/(?P<package>\w+)/(?P<version>[^/]+)/(?P<patchName>.*)$', + views.download_patch), + url(r'^$', views.frontpage), + ) diff --git a/patchtracker/views.py b/patchtracker/views.py new file mode 100644 index 0000000..0a316eb --- /dev/null +++ b/patchtracker/views.py @@ -0,0 +1,75 @@ +import os + +import django.db.models +import django.http +import django.shortcuts +import django.template + +import ComplexQueries +import Conf +import models + +def package_vers(request, package, version): + pkg = models.SourcePackage.objects.get( name=package, version=version ) + ctx = django.template.RequestContext(request) + tmpl = 'package_vers.html' + extra = { 'pkg':pkg, 'ctx':ctx, 'conf':Conf } + return django.shortcuts.render_to_response(tmpl, extra, context_instance=ctx) + +def display_toc(request, index): + packageIndex = ComplexQueries.PackageIndex(index) + tmpl = 'searchresults.html' + searchDescription = "Packages by index" + extra = { 'index':index, 'packageIndex':packageIndex, 'conf':Conf, + 'searchDescription':searchDescription, 'searchKey':index } + return django.shortcuts.render_to_response(tmpl, extra) + +def maintainer_search(request, maintainer): + mappings = models.SourcePackageMapping.objects.order_by('package__name').filter( django.db.models.Q(package__maintainer__contains=maintainer) | django.db.models.Q(package__uploaders__contains=maintainer ) ) + packageIndex = ComplexQueries.PackageIndexQuery( mappings ) + tmpl = 'searchresults.html' + searchDescription = "Maintainer email" + extra = { 'index':maintainer, 'packageIndex':packageIndex, 'conf':Conf, + 'searchDescription':searchDescription, 'searchKey':maintainer } + return django.shortcuts.render_to_response(tmpl, extra) + +def display_patch(request, patchType, package, version, patchName): + pkg = models.SourcePackage.objects.get( name=package, version=version ) + ctx = django.template.RequestContext(request) + tmpl = 'patch_view.html' + + if patchType == "debianonly": + patch = pkg.diffhandler().debiandir() + patchTitle = "debian-dir only changes" + elif patchType == "misc": + patch = pkg.diffhandler().filterdiff(include=patchName) + patchTitle = patchName + elif patchType == "nondebian": + patch = pkg.diffhandler().nondebiandir() + patchTitle = "direct (non-packaging) changes" + elif patchType == "series": + patch = pkg.diffhandler().series().fetch(patchName) + patchTitle = patchName + + extra = { 'pkg':pkg, 'patch':patch, 'patchType':patchType, 'conf':Conf, + 'patchName':patchName, 'patchTitle':patchTitle } + return django.shortcuts.render_to_response(tmpl, extra, context_instance=ctx) + +def download_patch(request, patchType, package, version, patchName): + pkg = models.SourcePackage.objects.get( name=package, version=version ) + if patchType == "debianonly": + patch = pkg.diffhandler().debiandir() + elif patchType == "misc": + patch = pkg.diffhandler().filterdiff(include=patchName) + elif patchType == "nondebian": + patch = pkg.diffhandler().nondebiandir() + elif patchType == "series": + patch = pkg.diffhandler().series().fetch(patchName) + return django.http.HttpResponse(patch, mimetype="text/plain") + +def frontpage(request): + nonlibs = [idx for idx in models.SourcePackage.objects.filter(~django.db.models.Q(name__startswith='lib')).extra(select={'index' : "SUBSTR(name, 1, 1)"}).distinct().values_list('index', flat=True)] + libs = [idx for idx in models.SourcePackage.objects.filter(name__startswith='lib').extra(select={'index' : "SUBSTR(name, 1, 4)"}).distinct().values_list('index', flat=True)] + indices = sorted(nonlibs + libs) + extra = { 'indices':indices, 'conf':Conf } + return django.shortcuts.render_to_response('frontpage.html', extra) diff --git a/reprepro/conf/diffsonly.py b/reprepro/conf/diffsonly.py index d2f02ea..460cb2b 100755 --- a/reprepro/conf/diffsonly.py +++ b/reprepro/conf/diffsonly.py @@ -1,6 +1,10 @@ #!/usr/bin/python -from debian_bundle import deb822 +try: + from debian import deb822 +except ImportError: + from debian_bundle import deb822 + from gzip import GzipFile from fnmatch import fnmatch import sys @@ -39,5 +43,5 @@ if __name__ == '__main__': if fnmatch(f['name'], wanted_glob): newfiles.append(f) ent[k] = newfiles - outf.write(str(ent)) + outf.write(unicode(ent).encode('utf-8')) outf.write("\n") diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..81ab0b6 --- /dev/null +++ b/settings.py @@ -0,0 +1,88 @@ +# Django settings for debianpatchtracker project. + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + # ('Your Name', 'your_email@domain.com'), +) + +MANAGERS = ADMINS + +DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. +DATABASE_NAME = 'pt.db' # Or path to database file if using sqlite3. +DATABASE_USER = '' # Not used with sqlite3. +DATABASE_PASSWORD = '' # Not used with sqlite3. +DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. +DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# If running in a Windows environment this must be set to the same as your +# system time zone. +TIME_ZONE = 'America/Chicago' + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-us' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# Absolute path to the directory that holds media. +# Example: "/home/media/media.lawrence.com/" +MEDIA_ROOT = '' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash if there is a path component (optional in other cases). +# Examples: "http://media.lawrence.com", "http://example.com/media/" +MEDIA_URL = '' + +# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a +# trailing slash. +# Examples: "http://foo.com/media/", "/media/". +ADMIN_MEDIA_PREFIX = '/media/' + +# Make this unique, and don't share it with anybody. +# set in non-versioned localsettings.py +#SECRET_KEY = '' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.load_template_source', + 'django.template.loaders.app_directories.load_template_source', +# 'django.template.loaders.eggs.load_template_source', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', +) + +ROOT_URLCONF = 'urls' + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. + '/home/sean/debian/patch-tracker/templates' +) + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.admin', + 'patchtracker' +) + +try: + from localsettings import * +except ImportError: + pass diff --git a/templates/skeleton.tmpl b/templates/base.html index 745dbcf..aa832ad 100644 --- a/templates/skeleton.tmpl +++ b/templates/base.html @@ -2,23 +2,21 @@ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> - <title>$title</title> + <title>{% block title %}{% endblock %}</title> <link rel="stylesheet" type="text/css" - href="$conf.root_url/static/css/patches.css"/> - <base href="$conf.root_url/" /> + href="/static/css/patches.css"/> </head> <body> <div class="pageheader"> - <img alt="[patchlogo]" src="$conf.root_url/static/img/swirlpatch.png"/> - <p> <a href="$conf.root_url/">Debian patch tracking system </a> </p> + <img alt="[patchlogo]" src="/static/img/swirlpatch.png"/> + <p> <a href="/">Debian patch tracking system </a> </p> <div class="breadcrumb"> - #set $crumblinks = [$link($conf.root_url+"/"+b[0],b[1]) for b in $crumbs] - #for $i in $crumblinks - - $i - #end for + {% for crumb in crumbs %} + {{ crumb }} + {% endfor %} </div> <!-- breadcrumb --> </div> - $body + {% block content %}{% endblock %} <div class="pagefooter"> page code/design/content is copyright (c) 2008 sean finney <seanius@debian.org>. <br/> diff --git a/templates/frontpage.tmpl b/templates/frontpage.html index 7b103a0..517feb7 100644 --- a/templates/frontpage.tmpl +++ b/templates/frontpage.html @@ -1,9 +1,8 @@ -#import templates -#extends templates.skeleton -#def title +{% extends "base.html" %} +{% block title %} Debian Project patch tracking system -#end def -#def body +{% endblock %} +{% block content %} <h1>Debian Project patch tracking system</h1> <h2>Introduction</h2> <p> @@ -12,18 +11,24 @@ Debian Project patch tracking system </p> <h2>Quick access</h2> <ul> - <li><span class="url">$conf.root_url/package/<p></span> + <li><span class="url">{{ conf.root_url }}/package/<p></span> - search for package p</li> - <li><span class="url">$conf.root_url/package/<p>/<v></span> + <li><span class="url">{{ conf.root_url }}/package/<p>/<v></span> - go directly to the page for package p, version v</li> - <li><span class="url">$conf.root_url/email/<u></span> + <li><span class="url">{{ conf.root_url }}/email/<u></span> - show packages with maintainer address containing u.</li> </ul> <h3>Browse patches by package name</h3> -#set $links = [ "<a href=\"index/"+k+"\">"+k+"</a>" for k in $indices ] -#set $out = " - ".join($links) <div class="quicklinks"> - $out + {% for index in indices %} + <a href="{% url patchtracker.views.display_toc index=index %}">{{ index }}</a> + {% if not forloop.last %} + - + {% endif %} + {% empty %} + Error fetching indices of available packages + {% endfor %} + <!-- <a href="index/"{{ k }}">{{ k }}</a> --> </div> <h3>Jump to package / Package search</h3> <div class="framed"> @@ -32,5 +37,4 @@ Debian Project patch tracking system <input type="submit" name="submit" /> </form> </div> - -#end def +{% endblock %} diff --git a/templates/letter_toc.tmpl b/templates/letter_toc.tmpl deleted file mode 100644 index a7bf9a1..0000000 --- a/templates/letter_toc.tmpl +++ /dev/null @@ -1,34 +0,0 @@ -#import templates -#extends templates.skeleton -#def title -Debian Project patch tracking system -#end def -#def body -<h1>Debian Project patch tracking system</h1> -<h2>Packaging patches by index - $idx</h2> -<table class="packagelisting"> -<tr> -<th>package</th> -#for $d in $dists - <th>$d</th> -#end for -</tr> -#for $p in $sorted($pkgs.iterkeys) -<tr> - <td> - <a name="$escape_name($p)" /> - <a href="http://packages.debian.org/$p">$p</a> - </td> - #for $d in $dists - <td> - #if $pkgs[$p].has_key($d) - <a href="$conf.root_url/package/$p/$pkgs[$p][$d].version">$wrappable_version($pkgs[$p][$d].version)</a> - #else - <i>n/a</i> - #end if - </td> - #end for -</tr> -#end for -</table> -#end def diff --git a/templates/package_vers.html b/templates/package_vers.html new file mode 100644 index 0000000..7e4c1cb --- /dev/null +++ b/templates/package_vers.html @@ -0,0 +1,148 @@ +{% extends "base.html" %} +{% block title %} +debian specific patch information for {{ pkg.name }} / {{ pkg.version }} +{% endblock %} +{% block content %} + <h1> + debian specific patch information for {{ pkg.name }} / {{ pkg.version }} + </h1> + <div class="diffsummary"> + <a name="diff-summary"></a> + <h2> Summary </h2> + <table class="summary"> + <tr> + <th>Package Version</th> + <th>Package Type</th> + <th>Source Package Format</th> + </tr> + <tr> + <td>{{ pkg.version }}</td> + <td>{{ pkg.type }}</td> + <td>{{ pkg.format }}</td> + </tr> + </table> + </div> <!-- diffsummary --> + +{% if pkg.debtar_name %} + <div class="debdiff"> + <a name="debian-tarball"></a> + <h2> Debian packaging Information </h2> + <table class="patchlisting"> + <tr> + <th>Debian changes tarfile</th> + <td colspan="2"> + <a href="{{ conf.archive_root_url }}/{{ pkg.loc }}/{{ pkg.debtar_name }}"> + {{ pkg.debtar_name }} + </a> + </td> + </tr> + <tr> + <th>Size</th><td colspan="2">{{ pkg.debtar_size }}</td> + </tr> + <tr> + <th>MD5sum</th><td colspan="2">{{ pkg.debtar_md5sum }}</td> + </tr> + </table> + </div> <!-- debdiff --> +{% endif %} +{% if pkg.diffgz_name %} + <div class="debdiff"> + <a name="debian-patches"></a> + <h2> Debian packaging Information </h2> + <table class="patchlisting"> + <tr> + <th>Diff file</th> + <td colspan="2"> + <a href="{{ conf.archive_root_url }}/{{ pkg.loc }}/{{ pkg.diffgz_name }}"> + {{ pkg.diffgz_name }} + </a> + </td> + </tr> + <tr> + <th>Size</th><td colspan="2">{{ pkg.diffgz_size }}</td> + </tr> + <tr> + <th>MD5sum</th><td colspan="2">{{ pkg.diffgz_md5sum }}</td> + </tr> + <tr> + <th>./debian only changes</th> + <td> + <a href="{{ conf.root_url }}/patch/debianonly/view/{{ pkg.name }}/{{ pkg.version }}">view</a> + </td> + <td> + <a href="{{ conf.root_url }}/patch/debianonly/dl/{{ pkg.name }}/{{ pkg.version }}">download</a> + </td> + </tr> + {% if pkg.diffgz_name and pkg.diffhandler.nondebiandir.lines %} + <tr> + <th>non packaging (i.e. not ./debian) changes</th> + <td> + <a href="{{ conf.root_url }}/patch/nondebian/view/{{ pkg.name }}/{{ pkg.version }}">view</a> + </td> + <td> + <a href="{{ conf.root_url }}/patch/nondebian/dl/{{ pkg.name }}/{{ pkg.version }}">download</a> + </td> + </tr> + {% endif %} + </table> + </div> <!-- debdiff --> +{% endif %} + +{% if pkg.diffgz_name or pkg.debtar_name %} + {% if pkg.diffhandler.series %} + <div class="debseries"> + <a name="series-patches"></a> + <h2> "series" style patches </h2> + <table class="patchlisting"> + <tr> + <th>patch</th> + <th>summary</th> + <th>view</th> + <th>raw</th> + </tr> + {% for name,patch in pkg.diffhandler.series.iterpatches %} + <tr> + <td>{{ name }}</td> + <td class="diffstat"><pre>{{ patch.diffstat }}</pre></td> + <td> + <a href="{{ conf.root_url }}/patch/series/view/{{ pkg.name }}/{{ pkg.version }}/{{ name }}">view</a> + </td> + <td> + <a href="{{ conf.root_url }}/patch/series/dl/{{ pkg.name }}/{{ pkg.version }}/{{ name }}">download</a> + </td> + </tr> + {% endfor %} + </table> + </div> <!-- debseries --> + {% endif %} +{% endif %} + +{% if pkg.diffgz_name and pkg.diffhandler.nondebiandir.lines %} + <div class="nondebdiff"> + <a name="direct-patches"></a> + <h2> Misc. Non-packaging "direct" style patches </h2> + <table class="patchlisting"> + <tr> + <th>file</th> + <th>inserted</th> + <th>deleted</th> + <th>modified</th> + <th>view</th> + <th>download</th> + </tr> + {% for insd,deld,modd,f in pkg.diffhandler.nondebiandir.diffstat.stats %} + <tr> + <td>{{ f }}</td><td>{{ insd }}</td><td>{{ deld }}</td><td>{{ modd }}</td> + <td> + <a href="{{ conf.root_url }}/patch/misc/view/{{ pkg.name }}/{{ pkg.version }}/{{ f }}">view</a> + </td> + <td> + <a href="{{ conf.root_url }}/patch/misc/dl/{{ pkg.name }}/{{ pkg.version }}/{{ f }}">download</a> + </td> + </tr> + {% endfor %} + </table> + </div> <!-- nondebdiff --> +{% endif %} + +{% endblock %} diff --git a/templates/package_vers.tmpl b/templates/package_vers.tmpl deleted file mode 100755 index 23ab83e..0000000 --- a/templates/package_vers.tmpl +++ /dev/null @@ -1,149 +0,0 @@ -#import templates -#extends templates.skeleton -#def title -debian specific patch information for $src.name / $src.version -#end def -#def body - #set $u = $conf.root_url - <h1>debian specific patch information for $src.name / $src.version</h1> - <div class="diffsummary"> - <a name="diff-summary"></a> - <h2> Summary </h2> - <table class="summary"> - <tr> - <th>Package Version</th> - <th>Package Type</th> - <th>Source Package Format</th> - </tr> - <tr> - <td>$src.version</td> - <td>$src.type</td> - <td>$src.format</td> - </tr> - </table> - </div> <!-- diffsummary --> - -#if $src.debtar_name - <div class="debdiff"> - <a name="debian-tarball"></a> - <h2> Debian packaging Information </h2> - <table class="patchlisting"> - <tr> - <th>Debian changes tarfile</th> - <td colspan="2"> - <a href="$conf.archive_root_url/$src.loc/$src.debtar_name"> - $src.debtar_name - </a> - </td> - </tr> - <tr> - <th>Size</th><td colspan="2">$src.debtar_size</td> - </tr> - <tr> - <th>MD5sum</th><td colspan="2">$src.debtar_md5sum</td> - </tr> - </table> - </div> <!-- debdiff --> -#end if - -#if $src.diffgz_name - <div class="debdiff"> - <a name="debian-patches"></a> - <h2> Debian packaging Information </h2> - <table class="patchlisting"> - <tr> - <th>Diff file</th> - <td colspan="2"> - <a href="$conf.archive_root_url/$src.loc/$src.diffgz_name"> - $src.diffgz_name - </a> - </td> - </tr> - <tr> - <th>Size</th><td colspan="2">$src.diffgz_size</td> - </tr> - <tr> - <th>MD5sum</th><td colspan="2">$src.diffgz_md5sum</td> - </tr> - <tr> - <th>./debian only changes</th> - <td> - <a href="$u/patch/debianonly/view/$src.name/$src.version">view</a> - </td> - <td> - <a href="$u/patch/debianonly/dl/$src.name/$src.version">download</a> - </td> - </tr> - #if $src.diffgz_name and $diffhandler.nondebiandir.lines - <tr> - <th>non packaging (i.e. not ./debian) changes</th> - <td> - <a href="$u/patch/nondebian/view/$src.name/$src.version">view</a> - </td> - <td> - <a href="$u/patch/nondebian/dl/$src.name/$src.version">download</a> - </td> - </tr> - #end if - </table> - </div> <!-- debdiff --> - #end if - -#if $src.diffgz_name and $diffhandler.nondebiandir.lines - <div class="nondebdiff"> - <a name="direct-patches"></a> - <h2> Misc. Non-packaging "direct" style patches </h2> - <table class="patchlisting"> - <tr> - <th>file</th> - <th>inserted</th> - <th>deleted</th> - <th>modified</th> - <th>view</th> - <th>download</th> - </tr> - #for $insd,$deld,$modd,$f in $diffhandler.nondebiandir.diffstat.stats - <tr> - <td>$f</td><td>$insd</td><td>$deld</td><td>$modd</td> - <td> - <a href="$u/patch/misc/view/$src.name/$src.version/$f">view</a> - </td> - <td> - <a href="$u/patch/misc/dl/$src.name/$src.version/$f">download</a> - </td> - </tr> - #end for - </table> - </div> <!-- nondebdiff --> -#end if - -#if $src.diffgz_name or $src.debtar_name -#set $series = $diffhandler.series -#if $series - <div class="debseries"> - <a name="series-patches"></a> - <h2> "series" style patches </h2> - <table class="patchlisting"> - <tr> - <th>patch</th> - <th>summary</th> - <th>view</th> - <th>raw</th> - </tr> - #for $name,$patch in $series.iterpatches() - <tr> - <td>$name</td> - <td class="diffstat"><pre>$patch.diffstat</pre></td> - <td> - <a href="$u/patch/series/view/$src.name/$src.version/$name">view</a> - </td> - <td> - <a href="$u/patch/series/dl/$src.name/$src.version/$name">download</a> - </td> - </tr> - #end for - </table> - </div> <!-- debseries --> -#end if -#end if -#end def diff --git a/templates/patch_view.html b/templates/patch_view.html new file mode 100644 index 0000000..743c536 --- /dev/null +++ b/templates/patch_view.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} +{% block title %} +Patch information for {{ pkg.name }} {{ pkg.version }}: {{ patchTitle }} +{% endblock %} +{% block content %} + <h1>{{ pkg.name }} {{ pkg.version }}: {{ patchTitle }}</h1> + <h2>Summary</h2> + <div> + <code class="diffstat"> + <pre> +{{ patch.diffstat|escape }} + </pre> + </code> + </div> + <div> + <a href="{{ conf.root_url }}/patch/{{ patchType }}/dl/{{ pkg.name }}/{{ pkg.version }}/{{ patchName }}"> + download this patch + </a> + </div> + <h2>Patch contents</h2> + <div class="patch"> +{{ patch.highlight|safe }} + </div> +{% endblock %} diff --git a/templates/patch_view.tmpl b/templates/patch_view.tmpl deleted file mode 100644 index 0a715b9..0000000 --- a/templates/patch_view.tmpl +++ /dev/null @@ -1,33 +0,0 @@ -#import templates -#extends templates.skeleton -#import pygments -#from pygments.lexers import DiffLexer -#from pygments.formatters import HtmlFormatter -#from cgi import escape -#def title -Patch information for $package ($version) $name -#end def -#def body - <h1>$package ($version) $name</h1> - <h2>Summary</h2> - <div> - <code class="diffstat"> - <pre> -$escape($str($patch.diffstat)) - </pre> - </code> - </div> - <div> - <a href="$conf.root_url/patch/$patchtype/dl/$package/$version/$name"> - download this patch - </a> - </div> - <h2>Patch contents</h2> - <div class="patch"> - #try - $pygments.highlight($str($patch).decode('utf-8'), $DiffLexer(), $HtmlFormatter(style='colorful', noclasses=True, encoding='utf-8')) - #except UnicodeDecodeError - $pygments.highlight($str($patch).decode('latin-1'), $DiffLexer(), $HtmlFormatter(style='colorful', noclasses=True, encoding='latin-1')) - #end try - </div> -#end def diff --git a/templates/searchresults.html b/templates/searchresults.html new file mode 100644 index 0000000..6f5abe5 --- /dev/null +++ b/templates/searchresults.html @@ -0,0 +1,33 @@ +{% extends "base.html" %} +{% block title %} +Debian Project patch tracking system +{% endblock %} +{% block content %} +<h1>Debian Project patch tracking system</h1> +<h2>Search Results: {{ searchDescription }} - {{ searchKey }}</h2> +<table class="packagelisting"> +<tr> +<th>package</th> +{% for dist in packageIndex.dists %} + <th>{{ dist }}</th> +{% endfor %} +</tr> +{% for row in packageIndex %} +<tr> + <td> + <a name="{{ row.package.anchor_name }}" /> + <a href="http://packages.debian.org/{{ row.package.name }}">{{ row.package.name }}</a> + </td> + {% for pkg in row.entries %} + <td> + {% if pkg %} + <a href="{{ conf.root_url }}/package/{{ pkg.name }}/{{ pkg.version }}">{{ pkg.wrappable_version|safe }}</a> + {% else %} + <i>n/a</i> + {% endif %} + </td> + {% endfor %} +</tr> +{% endfor %} +</table> +{% endblock %} diff --git a/templates/searchresults.tmpl b/templates/searchresults.tmpl deleted file mode 100644 index 76bf5ff..0000000 --- a/templates/searchresults.tmpl +++ /dev/null @@ -1,34 +0,0 @@ -#import templates -#extends templates.skeleton -#def title -Debian Project patch tracking system - Search Results -#end def -#def body -<h1>Debian Project patch tracking system</h1> -<h2>Search Results - $searchtype: $idx</h2> -<table class="packagelisting"> -<tr> -<th>package</th> -#for $d in $dists - <th>$d</th> -#end for -</tr> -#for $p in $sorted($pkgs.iterkeys) -<tr> - <td> - <a name="$p" /> - <a href="http://packages.debian.org/$p">$p</a> - </td> - #for $d in $dists - <td> - #if $pkgs[$p].has_key($d) - <a href="$conf.root_url/package/$p/$pkgs[$p][$d].version">$pkgs[$p][$d].version</a> - #else - <i>n/a</i> - #end if - </td> - #end for -</tr> -#end for -</table> -#end def @@ -0,0 +1,15 @@ +import os + +from django.conf.urls.defaults import * +from django.contrib import admin +admin.autodiscover() + +urlpatterns = patterns('', + # admin interface (for debugging, mostly) + (r'^admin/', include(admin.site.urls)), + # static content + (r'^static/(?P<path>.*)$', 'django.views.static.serve', + {'document_root': os.path.sep.join([os.curdir, "static"])}), + + (r'^', include('patchtracker.urls')), +) |