3 # Various statistical pr0nography fun and games
4 # Copyright (C) 2000, 2001, 2002, 2003 James Troup <james@nocrew.org>
5 # $Id: saffron,v 1.2 2003-02-07 14:53:47 troup Exp $
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.
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.
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
21 ################################################################################
23 # <aj> can we change the standards instead?
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
30 # [aj's attempt to avoid ABI changes for released architecture(s)]
32 ################################################################################
38 ################################################################################
43 ################################################################################
45 def usage(exit_code=0):
46 print """Usage: saffron STAT
49 -h, --help show this help and exit.
51 The following STAT modes are available:
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
59 ################################################################################
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""");
68 q = projectB.query("SELECT sum(size) FROM files WHERE filename ~ '.(diff.gz|tar.gz|dsc)$'");
71 ################################################################################
73 def daily_install_stats():
75 file = utils.open_file("2001-11");
76 for line in file.readlines():
77 split = line.strip().split('~');
79 if program != "katie":
82 if action != "installing changes" and action != "installed":
85 if not stats.has_key(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]);
97 packages = stats[date]["packages"]
98 size = int(stats[date]["size"] / 1024.0 / 1024.0)
99 print "%s %s %s" % (date, packages, size);
101 ################################################################################
111 def suite_sort(a, b):
112 a_priority = int(Cnf["Suite::%s::Priority" % (a)]);
113 b_priority = int(Cnf["Suite::%s::Priority" % (b)]);
114 return cmp(a_priority, b_priority);
116 def output_format(suite):
118 for word in suite.split("-"):
119 output_suite.append(word[0]);
120 return "-".join(output_suite);
122 # Obvious query with GROUP BY and mapped names -> 50 seconds
123 # GROUP BY but ids instead of suite/architecture names -> 28 seconds
124 # Simple query -> 14 seconds
125 # Simple query into large dictionary + processing -> 21 seconds
126 # Simple query into large pre-created dictionary + processing -> 18 seconds
128 def number_of_packages():
134 # Build up suite mapping
135 q = projectB.query("SELECT id, suite_name FROM suite");
136 suite_ql = q.getresult();
140 suite_ids[name] = id;
141 # Build up architecture mapping
142 q = projectB.query("SELECT id, arch_string FROM architecture");
143 for i in q.getresult():
147 # Pre-create the dictionary
148 for suite_id in suites.keys():
150 for arch_id in arches.keys():
151 d[suite_id][arch_id] = 0;
152 # Get the raw data for binaries
153 q = projectB.query("""
154 SELECT ba.suite, b.architecture
155 FROM binaries b, bin_associations ba
156 WHERE b.id = ba.bin""");
157 # Simultate 'GROUP by suite, architecture' with a dictionary
158 for i in q.getresult():
159 (suite_id, arch_id) = i;
160 d[suite_id][arch_id] = d[suite_id][arch_id] + 1;
161 # Get the raw data for source
162 arch_id = arch_ids["source"];
163 q = projectB.query("""
164 SELECT suite, count(suite) FROM src_associations GROUP BY suite;""");
165 for i in q.getresult():
166 (suite_id, count) = i;
167 d[suite_id][arch_id] = d[suite_id][arch_id] + count;
170 suite_list = suites.values();
171 suite_list.sort(suite_sort);
174 for suite in suite_list:
175 suite_id = suite_ids[suite];
176 suite_arches[suite_id] = {};
177 for arch in Cnf.ValueList("Suite::%s::Architectures" % (suite)):
178 suite_arches[suite_id][arch] = "";
179 suite_id_list.append(suite_id);
180 output_list = map(lambda x: output_format(x), suite_list);
181 longest_suite = longest(output_list);
182 arch_list = arches.values();
184 longest_arch = longest(arch_list);
186 output = (" "*longest_arch) + " |"
187 for suite in output_list:
188 output = output + suite.center(longest_suite)+" |";
189 output = output + "\n"+(len(output)*"-")+"\n";
191 arch_list = arches.values();
193 longest_arch = longest(arch_list);
194 for arch in arch_list:
195 arch_id = arch_ids[arch];
196 output = output + arch.center(longest_arch)+" |";
197 for suite_id in suite_id_list:
198 if suite_arches[suite_id].has_key(arch):
199 count = repr(d[suite_id][arch_id]);
202 output = output + count.rjust(longest_suite)+" |";
203 output = output + "\n";
206 ################################################################################
209 global Cnf, projectB;
211 Cnf = utils.get_conf();
212 Arguments = [('h',"help","Saffron::Options::Help")];
214 if not Cnf.has_key("Saffron::Options::%s" % (i)):
215 Cnf["Saffron::Options::%s" % (i)] = "";
217 args = apt_pkg.ParseCommandLine(Cnf, Arguments, sys.argv);
219 Options = Cnf.SubTree("Saffron::Options")
224 utils.warn("saffron requires at least one argument");
227 utils.warn("saffron accepts only one argument");
229 mode = args[0].lower();
231 projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"]));
233 if mode == "arch-space":
234 per_arch_space_use();
235 elif mode == "pkg-nums":
236 number_of_packages();
237 elif mode == "daily-install":
238 daily_install_stats();
240 utils.warn("unknown mode '%s'" % (mode));
243 ################################################################################
245 if __name__ == '__main__':