X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;ds=sidebyside;f=saffron;fp=saffron;h=a9e759b11699aacb51ce2cb7b7d72fa4a247a373;hb=f6386a74a3399e05382412c0eb23d9b301f6e6d4;hp=0000000000000000000000000000000000000000;hpb=fa7b68ee3d2c9d467669021fd62e09a407e1244a;p=dak.git diff --git a/saffron b/saffron new file mode 100755 index 00000000..a9e759b1 --- /dev/null +++ b/saffron @@ -0,0 +1,234 @@ +#!/usr/bin/env python + +# Various statistical pr0nography fun and games +# Copyright (C) 2000, 2001, 2002 James Troup +# $Id: saffron,v 1.1 2003-01-02 18:06:19 troup Exp $ + +# 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 pg, sys; +import utils; +import apt_pkg; + +################################################################################ + +Cnf = None; +projectB = None; + +################################################################################ + +def usage(exit_code=0): + print """Usage: saffron STAT +Print various stats. + + -h, --help show this help and exit. + +The following STAT modes are available: + + arch-space - displays space used by each architecture + pkg-nums - displays the number of packages by suite/architecture + daily-install - displays daily install stats suitable for graphing +""" + sys.exit(exit_code) + +################################################################################ + +def per_arch_space_use(): + q = projectB.query(""" +SELECT a.arch_string as Architecture, sum(f.size) + FROM files f, binaries b, architecture a + WHERE a.id=b.architecture AND f.id=b.file + GROUP BY a.arch_string"""); + print q; + +################################################################################ + +def daily_install_stats(): + stats = {}; + file = utils.open_file("2001-11"); + for line in file.readlines(): + split = line.strip().split('~'); + program = split[1]; + if program != "katie": + continue; + action = split[2]; + if action != "installing changes" and action != "installed": + continue; + date = split[0][:8]; + if not stats.has_key(date): + stats[date] = {}; + stats[date]["packages"] = 0; + stats[date]["size"] = 0.0; + if action == "installing changes": + stats[date]["packages"] += 1; + elif action == "installed": + stats[date]["size"] += float(split[5]); + + dates = stats.keys(); + dates.sort(); + for date in dates: + packages = stats[date]["packages"] + size = int(stats[date]["size"] / 1024.0 / 1024.0) + print "%s %s %s" % (date, packages, size); + +################################################################################ + +def longest(list): + longest = 0; + for i in list: + l = len(i); + if l > longest: + longest = l; + return longest; + +def suite_sort(a, b): + a_priority = int(Cnf["Suite::%s::Priority" % (a)]); + b_priority = int(Cnf["Suite::%s::Priority" % (b)]); + return cmp(a_priority, b_priority); + +def output_format(suite): + output_suite = []; + for word in suite.split("-"): + output_suite.append(word[0]); + return output_suite.join("-"); + +# Obvious query with GROUP BY and mapped names -> 50 seconds +# GROUP BY but ids instead of suite/architecture names -> 28 seconds +# Simple query -> 14 seconds +# Simple query into large dictionary + processing -> 21 seconds +# Simple query into large pre-created dictionary + processing -> 18 seconds + +def number_of_packages(): + arches = {}; + arch_ids = {}; + suites = {}; + suite_ids = {}; + d = {}; + # Build up suite mapping + q = projectB.query("SELECT id, suite_name FROM suite"); + suite_ql = q.getresult(); + for i in suite_ql: + (id, name) = i; + suites[id] = name; + suite_ids[name] = id; + # Build up architecture mapping + q = projectB.query("SELECT id, arch_string FROM architecture"); + for i in q.getresult(): + (id, name) = i; + arches[id] = name; + arch_ids[name] = id; + # Pre-create the dictionary + for suite_id in suites.keys(): + d[suite_id] = {}; + for arch_id in arches.keys(): + d[suite_id][arch_id] = 0; + # Get the raw data for binaries + q = projectB.query(""" +SELECT ba.suite, b.architecture + FROM binaries b, bin_associations ba + WHERE b.id = ba.bin"""); + # Simultate 'GROUP by suite, architecture' with a dictionary + for i in q.getresult(): + (suite_id, arch_id) = i; + d[suite_id][arch_id] = d[suite_id][arch_id] + 1; + # Get the raw data for source + arch_id = arch_ids["source"]; + q = projectB.query(""" +SELECT suite, count(suite) FROM src_associations GROUP BY suite;"""); + for i in q.getresult(): + (suite_id, count) = i; + d[suite_id][arch_id] = d[suite_id][arch_id] + count; + ## Print the results + # Setup + suite_list = suites.values(); + suite_list.sort(suite_sort); + suite_id_list = []; + suite_arches = {}; + for suite in suite_list: + suite_id = suite_ids[suite]; + suite_arches[suite_id] = {}; + for arch in Cnf.ValueList("Suite::%s::Architectures" % (suite)): + suite_arches[suite_id][arch] = ""; + suite_id_list.append(suite_id); + output_list = map(lambda x: output_format(x), suite_list); + longest_suite = longest(output_list); + arch_list = arches.values(); + arch_list.sort(); + longest_arch = longest(arch_list); + # Header + output = (" "*longest_arch) + " |" + for suite in output_list: + output = output + suite.center(longest_suite)+" |"; + output = output + "\n"+(len(output)*"-")+"\n"; + # per-arch data + arch_list = arches.values(); + arch_list.sort(); + longest_arch = longest(arch_list); + for arch in arch_list: + arch_id = arch_ids[arch]; + output = output + arch.center(longest_arch)+" |"; + for suite_id in suite_id_list: + if suite_arches[suite_id].has_key(arch): + count = repr(d[suite_id][arch_id]); + else: + count = "-"; + output = output + count.rjust(longest_suite)+" |"; + output = output + "\n"; + print output; + +################################################################################ + +def main (): + global Cnf, projectB; + + Cnf = utils.get_conf(); + Arguments = [('h',"help","Saffron::Options::Help")]; + for i in [ "help" ]: + if not Cnf.has_key("Saffron::Options::%s" % (i)): + Cnf["Saffron::Options::%s" % (i)] = ""; + + args = apt_pkg.ParseCommandLine(Cnf, Arguments, sys.argv); + + Options = Cnf.SubTree("Saffron::Options") + if Options["Help"]: + usage(); + + if len(args) < 1: + utils.warn("saffron requires at least one argument"); + usage(1); + elif len(args) > 1: + utils.warn("saffron accepts only one argument"); + usage(1); + mode = args[0].lower(); + + projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"])); + + if mode == "arch-space": + per_arch_space_use(); + elif mode == "pkg-nums": + number_of_packages(); + elif mode == "daily-install": + daily_install_stats(); + else: + utils.warn("unknown mode '%s'" % (mode)); + usage(1); + +################################################################################ + +if __name__ == '__main__': + main() +