]> git.decadent.org.uk Git - dak.git/blob - saffron
Add new top level directories
[dak.git] / saffron
1 #!/usr/bin/env python
2
3 # Various statistical pr0nography fun and games
4 # Copyright (C) 2000, 2001, 2002, 2003  James Troup <james@nocrew.org>
5 # $Id: saffron,v 1.3 2005-11-15 09:50:32 ajt 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 # <aj>    can we change the standards instead?
24 # <neuro> standards?
25 # <aj>    whatever we're not conforming to
26 # <aj>    if there's no written standard, why don't we declare linux as
27 #         the defacto standard
28 # <aj>    go us!
29
30 # [aj's attempt to avoid ABI changes for released architecture(s)]
31
32 ################################################################################
33
34 import pg, sys;
35 import utils;
36 import apt_pkg;
37
38 ################################################################################
39
40 Cnf = None;
41 projectB = None;
42
43 ################################################################################
44
45 def usage(exit_code=0):
46     print """Usage: saffron STAT
47 Print various stats.
48
49   -h, --help                show this help and exit.
50
51 The following STAT modes are available:
52
53   arch-space    - displays space used by each architecture
54   pkg-nums      - displays the number of packages by suite/architecture
55   daily-install - displays daily install stats suitable for graphing
56 """
57     sys.exit(exit_code)
58
59 ################################################################################
60
61 def per_arch_space_use():
62     q = projectB.query("""
63 SELECT a.arch_string as Architecture, sum(f.size)
64   FROM files f, binaries b, architecture a
65   WHERE a.id=b.architecture AND f.id=b.file
66   GROUP BY a.arch_string""");
67     print q;
68     q = projectB.query("SELECT sum(size) FROM files WHERE filename ~ '.(diff.gz|tar.gz|dsc)$'");
69     print q;
70
71 ################################################################################
72
73 def daily_install_stats():
74     stats = {};
75     file = utils.open_file("2001-11");
76     for line in file.readlines():
77         split = line.strip().split('~');
78         program = split[1];
79         if program != "katie":
80             continue;
81         action = split[2];
82         if action != "installing changes" and action != "installed":
83             continue;
84         date = split[0][:8];
85         if not stats.has_key(date):
86             stats[date] = {};
87             stats[date]["packages"] = 0;
88             stats[date]["size"] = 0.0;
89         if action == "installing changes":
90             stats[date]["packages"] += 1;
91         elif action == "installed":
92             stats[date]["size"] += float(split[5]);
93
94     dates = stats.keys();
95     dates.sort();
96     for date in dates:
97         packages = stats[date]["packages"]
98         size = int(stats[date]["size"] / 1024.0 / 1024.0)
99         print "%s %s %s" % (date, packages, size);
100
101 ################################################################################
102
103 def longest(list):
104     longest = 0;
105     for i in list:
106         l = len(i);
107         if l > longest:
108             longest = l;
109     return longest;
110
111 def suite_sort(a, b):
112     if Cnf.has_key("Suite::%s::Priority" % (a)):
113         a_priority = int(Cnf["Suite::%s::Priority" % (a)]);
114     else:
115         a_priority = 0;
116     if Cnf.has_key("Suite::%s::Priority" % (b)):
117         b_priority = int(Cnf["Suite::%s::Priority" % (b)]);
118     else:
119         b_priority = 0;
120     return cmp(a_priority, b_priority);
121
122 def output_format(suite):
123     output_suite = [];
124     for word in suite.split("-"):
125         output_suite.append(word[0]);
126     return "-".join(output_suite);
127
128 # Obvious query with GROUP BY and mapped names                  -> 50 seconds
129 # GROUP BY but ids instead of suite/architecture names          -> 28 seconds
130 # Simple query                                                  -> 14 seconds
131 # Simple query into large dictionary + processing               -> 21 seconds
132 # Simple query into large pre-created dictionary + processing   -> 18 seconds
133
134 def number_of_packages():
135     arches = {};
136     arch_ids = {};
137     suites = {};
138     suite_ids = {};
139     d = {};
140     # Build up suite mapping
141     q = projectB.query("SELECT id, suite_name FROM suite");
142     suite_ql = q.getresult();
143     for i in suite_ql:
144         (id, name) = i;
145         suites[id] = name;
146         suite_ids[name] = id;
147     # Build up architecture mapping
148     q = projectB.query("SELECT id, arch_string FROM architecture");
149     for i in q.getresult():
150         (id, name) = i;
151         arches[id] = name;
152         arch_ids[name] = id;
153     # Pre-create the dictionary
154     for suite_id in suites.keys():
155         d[suite_id] = {};
156         for arch_id in arches.keys():
157             d[suite_id][arch_id] = 0;
158     # Get the raw data for binaries
159     q = projectB.query("""
160 SELECT ba.suite, b.architecture
161   FROM binaries b, bin_associations ba
162  WHERE b.id = ba.bin""");
163     # Simultate 'GROUP by suite, architecture' with a dictionary
164     for i in q.getresult():
165         (suite_id, arch_id) = i;
166         d[suite_id][arch_id] = d[suite_id][arch_id] + 1;
167     # Get the raw data for source
168     arch_id = arch_ids["source"];
169     q = projectB.query("""
170 SELECT suite, count(suite) FROM src_associations GROUP BY suite;""");
171     for i in q.getresult():
172         (suite_id, count) = i;
173         d[suite_id][arch_id] = d[suite_id][arch_id] + count;
174     ## Print the results
175     # Setup
176     suite_list = suites.values();
177     suite_list.sort(suite_sort);
178     suite_id_list = [];
179     suite_arches = {};
180     for suite in suite_list:
181         suite_id = suite_ids[suite];
182         suite_arches[suite_id] = {};
183         for arch in Cnf.ValueList("Suite::%s::Architectures" % (suite)):
184             suite_arches[suite_id][arch] = "";
185         suite_id_list.append(suite_id);
186     output_list = map(lambda x: output_format(x), suite_list);
187     longest_suite = longest(output_list);
188     arch_list = arches.values();
189     arch_list.sort();
190     longest_arch = longest(arch_list);
191     # Header
192     output = (" "*longest_arch) + " |"
193     for suite in output_list:
194         output = output + suite.center(longest_suite)+" |";
195     output = output + "\n"+(len(output)*"-")+"\n";
196     # per-arch data
197     arch_list = arches.values();
198     arch_list.sort();
199     longest_arch = longest(arch_list);
200     for arch in arch_list:
201         arch_id = arch_ids[arch];
202         output = output + arch.center(longest_arch)+" |";
203         for suite_id in suite_id_list:
204             if suite_arches[suite_id].has_key(arch):
205                 count = repr(d[suite_id][arch_id]);
206             else:
207                 count = "-";
208             output = output + count.rjust(longest_suite)+" |";
209         output = output + "\n";
210     print output;
211
212 ################################################################################
213
214 def main ():
215     global Cnf, projectB;
216
217     Cnf = utils.get_conf();
218     Arguments = [('h',"help","Saffron::Options::Help")];
219     for i in [ "help" ]:
220         if not Cnf.has_key("Saffron::Options::%s" % (i)):
221             Cnf["Saffron::Options::%s" % (i)] = "";
222
223     args = apt_pkg.ParseCommandLine(Cnf, Arguments, sys.argv);
224
225     Options = Cnf.SubTree("Saffron::Options")
226     if Options["Help"]:
227         usage();
228
229     if len(args) < 1:
230         utils.warn("saffron requires at least one argument");
231         usage(1);
232     elif len(args) > 1:
233         utils.warn("saffron accepts only one argument");
234         usage(1);
235     mode = args[0].lower();
236
237     projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"]));
238
239     if mode == "arch-space":
240         per_arch_space_use();
241     elif mode == "pkg-nums":
242         number_of_packages();
243     elif mode == "daily-install":
244         daily_install_stats();
245     else:
246         utils.warn("unknown mode '%s'" % (mode));
247         usage(1);
248
249 ################################################################################
250
251 if __name__ == '__main__':
252     main()
253