]> git.decadent.org.uk Git - dak.git/blob - saffron
Initial import
[dak.git] / saffron
1 #!/usr/bin/env python
2
3 # Various statistical pr0nography fun and games
4 # Copyright (C) 2000, 2001, 2002  James Troup <james@nocrew.org>
5 # $Id: saffron,v 1.1 2003-01-02 18:06:19 troup Exp $
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
21 ################################################################################
22
23 import pg, sys;
24 import utils;
25 import apt_pkg;
26
27 ################################################################################
28
29 Cnf = None;
30 projectB = None;
31
32 ################################################################################
33
34 def usage(exit_code=0):
35     print """Usage: saffron STAT
36 Print various stats.
37
38   -h, --help                show this help and exit.
39
40 The following STAT modes are available:
41
42   arch-space    - displays space used by each architecture
43   pkg-nums      - displays the number of packages by suite/architecture
44   daily-install - displays daily install stats suitable for graphing
45 """
46     sys.exit(exit_code)
47
48 ################################################################################
49
50 def per_arch_space_use():
51     q = projectB.query("""
52 SELECT a.arch_string as Architecture, sum(f.size)
53   FROM files f, binaries b, architecture a
54   WHERE a.id=b.architecture AND f.id=b.file
55   GROUP BY a.arch_string""");
56     print q;
57
58 ################################################################################
59
60 def daily_install_stats():
61     stats = {};
62     file = utils.open_file("2001-11");
63     for line in file.readlines():
64         split = line.strip().split('~');
65         program = split[1];
66         if program != "katie":
67             continue;
68         action = split[2];
69         if action != "installing changes" and action != "installed":
70             continue;
71         date = split[0][:8];
72         if not stats.has_key(date):
73             stats[date] = {};
74             stats[date]["packages"] = 0;
75             stats[date]["size"] = 0.0;
76         if action == "installing changes":
77             stats[date]["packages"] += 1;
78         elif action == "installed":
79             stats[date]["size"] += float(split[5]);
80
81     dates = stats.keys();
82     dates.sort();
83     for date in dates:
84         packages = stats[date]["packages"]
85         size = int(stats[date]["size"] / 1024.0 / 1024.0)
86         print "%s %s %s" % (date, packages, size);
87
88 ################################################################################
89
90 def longest(list):
91     longest = 0;
92     for i in list:
93         l = len(i);
94         if l > longest:
95             longest = l;
96     return longest;
97
98 def suite_sort(a, b):
99     a_priority = int(Cnf["Suite::%s::Priority" % (a)]);
100     b_priority = int(Cnf["Suite::%s::Priority" % (b)]);
101     return cmp(a_priority, b_priority);
102
103 def output_format(suite):
104     output_suite = [];
105     for word in suite.split("-"):
106         output_suite.append(word[0]);
107     return output_suite.join("-");
108
109 # Obvious query with GROUP BY and mapped names                  -> 50 seconds
110 # GROUP BY but ids instead of suite/architecture names          -> 28 seconds
111 # Simple query                                                  -> 14 seconds
112 # Simple query into large dictionary + processing               -> 21 seconds
113 # Simple query into large pre-created dictionary + processing   -> 18 seconds
114
115 def number_of_packages():
116     arches = {};
117     arch_ids = {};
118     suites = {};
119     suite_ids = {};
120     d = {};
121     # Build up suite mapping
122     q = projectB.query("SELECT id, suite_name FROM suite");
123     suite_ql = q.getresult();
124     for i in suite_ql:
125         (id, name) = i;
126         suites[id] = name;
127         suite_ids[name] = id;
128     # Build up architecture mapping
129     q = projectB.query("SELECT id, arch_string FROM architecture");
130     for i in q.getresult():
131         (id, name) = i;
132         arches[id] = name;
133         arch_ids[name] = id;
134     # Pre-create the dictionary
135     for suite_id in suites.keys():
136         d[suite_id] = {};
137         for arch_id in arches.keys():
138             d[suite_id][arch_id] = 0;
139     # Get the raw data for binaries
140     q = projectB.query("""
141 SELECT ba.suite, b.architecture
142   FROM binaries b, bin_associations ba
143  WHERE b.id = ba.bin""");
144     # Simultate 'GROUP by suite, architecture' with a dictionary
145     for i in q.getresult():
146         (suite_id, arch_id) = i;
147         d[suite_id][arch_id] = d[suite_id][arch_id] + 1;
148     # Get the raw data for source
149     arch_id = arch_ids["source"];
150     q = projectB.query("""
151 SELECT suite, count(suite) FROM src_associations GROUP BY suite;""");
152     for i in q.getresult():
153         (suite_id, count) = i;
154         d[suite_id][arch_id] = d[suite_id][arch_id] + count;
155     ## Print the results
156     # Setup
157     suite_list = suites.values();
158     suite_list.sort(suite_sort);
159     suite_id_list = [];
160     suite_arches = {};
161     for suite in suite_list:
162         suite_id = suite_ids[suite];
163         suite_arches[suite_id] = {};
164         for arch in Cnf.ValueList("Suite::%s::Architectures" % (suite)):
165             suite_arches[suite_id][arch] = "";
166         suite_id_list.append(suite_id);
167     output_list = map(lambda x: output_format(x), suite_list);
168     longest_suite = longest(output_list);
169     arch_list = arches.values();
170     arch_list.sort();
171     longest_arch = longest(arch_list);
172     # Header
173     output = (" "*longest_arch) + " |"
174     for suite in output_list:
175         output = output + suite.center(longest_suite)+" |";
176     output = output + "\n"+(len(output)*"-")+"\n";
177     # per-arch data
178     arch_list = arches.values();
179     arch_list.sort();
180     longest_arch = longest(arch_list);
181     for arch in arch_list:
182         arch_id = arch_ids[arch];
183         output = output + arch.center(longest_arch)+" |";
184         for suite_id in suite_id_list:
185             if suite_arches[suite_id].has_key(arch):
186                 count = repr(d[suite_id][arch_id]);
187             else:
188                 count = "-";
189             output = output + count.rjust(longest_suite)+" |";
190         output = output + "\n";
191     print output;
192
193 ################################################################################
194
195 def main ():
196     global Cnf, projectB;
197
198     Cnf = utils.get_conf();
199     Arguments = [('h',"help","Saffron::Options::Help")];
200     for i in [ "help" ]:
201         if not Cnf.has_key("Saffron::Options::%s" % (i)):
202             Cnf["Saffron::Options::%s" % (i)] = "";
203
204     args = apt_pkg.ParseCommandLine(Cnf, Arguments, sys.argv);
205
206     Options = Cnf.SubTree("Saffron::Options")
207     if Options["Help"]:
208         usage();
209
210     if len(args) < 1:
211         utils.warn("saffron requires at least one argument");
212         usage(1);
213     elif len(args) > 1:
214         utils.warn("saffron accepts only one argument");
215         usage(1);
216     mode = args[0].lower();
217
218     projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"]));
219
220     if mode == "arch-space":
221         per_arch_space_use();
222     elif mode == "pkg-nums":
223         number_of_packages();
224     elif mode == "daily-install":
225         daily_install_stats();
226     else:
227         utils.warn("unknown mode '%s'" % (mode));
228         usage(1);
229
230 ################################################################################
231
232 if __name__ == '__main__':
233     main()
234