1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
|
#!/usr/bin/python3
#
# Copyright (C) 2007 Enrico Zini <enrico@debian.org>
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
# Navigate among related Debian packages
import sys
# Requires python-extractor, python-magic and python-debtags
from debian import debtags
import re
from optparse import OptionParser
import apt
VERSION="0.1"
class Parser(OptionParser):
def __init__(self, *args, **kwargs):
OptionParser.__init__(self, *args, **kwargs)
def error(self, msg):
sys.stderr.write("%s: error: %s\n\n" % (self.get_prog_name(), msg))
self.print_help(sys.stderr)
sys.exit(2)
if __name__ == '__main__':
parser = Parser(usage="usage: %prog [options] pkgname",
version="%prog "+ VERSION,
description="walk through Debian packages")
parser.add_option("--tagdb", default="/var/lib/debtags/package-tags", help="Tag database to use (default: %default)")
(options, args) = parser.parse_args()
if len(args) == 0:
parser.error("Please provide the name of an initial package")
# Read full database
db = debtags.DB()
tag_filter = re.compile(r"^special::.+$|^.+::TODO$")
db.read(open(options.tagdb, "r"), lambda x: not tag_filter.match(x))
apt_cache = apt.Cache()
# Maximum number of previous packages to remember
maxlen = 5
# Initial package selection
trail = [ args[0] ]
# Loop until the user chooses to quit
done = False
while not done:
# Compute a package weight according to how old it is in the
# trail
pkgweight = {}
for idx, pkg in enumerate(trail):
pkgweight[pkg] = 1.-(idx/maxlen)
# For every tag, find the number of packages in trail that have the tag
tagscores = {}
for pkg in trail:
for tag in db.tags_of_package(pkg):
if tag in tagscores:
tagscores[tag] += pkgweight[pkg]
else:
tagscores[tag] = pkgweight[pkg]
# Divide every tag score by the number of packages in the trail,
# obtaining a 'tag weight'. A package can be later scored by summing
# the weight of all its tags.
for tag in tagscores:
tagscores[tag] = float(tagscores[tag]) / float(len(trail))
# Find the merged tagset of the packages in trail
trailtags = set(tagscores.keys())
# Get the list of packages whose tagsets intersect the trail tagset
nextpkgs = set()
for pkg, tags in db.iter_packages_tags():
if trailtags & tags:
nextpkgs.add(pkg)
# Score every package by the sum of the weight of its tags
def pkgscore(pkg):
score = 0.0
for tag in db.tags_of_package(pkg):
if tag in tagscores:
score += tagscores[tag]
return score
# Show the first 20 packages in reverse score order
#display = sorted(nextpkgs - set(trail), key=pkgscore, reverse=True)[:20]
display = sorted(nextpkgs, key=pkgscore, reverse=True)[:20]
for num, pkg in enumerate(display):
aptpkg = apt_cache[pkg]
desc = aptpkg.raw_description.split("\n")[0]
print("%2d) %s - %s" % (num + 1, pkg, desc))
# Ask the user to choose a new package
while True:
ans = input("> ").strip()
if ans[0] == 'q':
done = True
break
elif ans.isdigit():
num = int(ans) - 1
if num < len(display):
# TODO: on a different kind of interface, display the full
# description of pkg
trail = [display[num]] + trail[:maxlen]
break
else:
print("The number is too high")
# vim:set ts=4 sw=4:
|