3 # Various statistical pr0nography fun and games
4 # Copyright (C) 2000, 2001, 2002, 2003, 2006 James Troup <james@nocrew.org>
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 ################################################################################
22 # <aj> can we change the standards instead?
24 # <aj> whatever we're not conforming to
25 # <aj> if there's no written standard, why don't we declare linux as
26 # the defacto standard
29 # [aj's attempt to avoid ABI changes for released architecture(s)]
31 ################################################################################
35 from daklib import utils
37 ################################################################################
42 ################################################################################
44 def usage(exit_code=0):
45 print """Usage: dak stats MODE
48 -h, --help show this help and exit.
50 The following MODEs are available:
52 arch-space - displays space used by each architecture
53 pkg-nums - displays the number of packages by suite/architecture
54 daily-install - displays daily install stats suitable for graphing
58 ################################################################################
60 def per_arch_space_use():
61 q = projectB.query("""
62 SELECT a.arch_string as Architecture, sum(f.size)
63 FROM files f, binaries b, architecture a
64 WHERE a.id=b.architecture AND f.id=b.file
65 GROUP BY a.arch_string""")
67 q = projectB.query("SELECT sum(size) FROM files WHERE filename ~ '.(diff.gz|tar.gz|dsc)$'")
70 ################################################################################
72 def daily_install_stats():
74 f = utils.open_file("2001-11")
75 for line in f.readlines():
76 split = line.strip().split('|')
78 if program != "katie" and program != "process-accepted":
81 if action != "installing changes" and action != "installed":
84 if not stats.has_key(date):
86 stats[date]["packages"] = 0
87 stats[date]["size"] = 0.0
88 if action == "installing changes":
89 stats[date]["packages"] += 1
90 elif action == "installed":
91 stats[date]["size"] += float(split[5])
96 packages = stats[date]["packages"]
97 size = int(stats[date]["size"] / 1024.0 / 1024.0)
98 print "%s %s %s" % (date, packages, size)
100 ################################################################################
110 def suite_sort(a, b):
111 if Cnf.has_key("Suite::%s::Priority" % (a)):
112 a_priority = int(Cnf["Suite::%s::Priority" % (a)])
115 if Cnf.has_key("Suite::%s::Priority" % (b)):
116 b_priority = int(Cnf["Suite::%s::Priority" % (b)])
119 return cmp(a_priority, b_priority)
121 def output_format(suite):
123 for word in suite.split("-"):
124 output_suite.append(word[0])
125 return "-".join(output_suite)
127 # Obvious query with GROUP BY and mapped names -> 50 seconds
128 # GROUP BY but ids instead of suite/architecture names -> 28 seconds
129 # Simple query -> 14 seconds
130 # Simple query into large dictionary + processing -> 21 seconds
131 # Simple query into large pre-created dictionary + processing -> 18 seconds
133 def number_of_packages():
139 # Build up suite mapping
140 q = projectB.query("SELECT id, suite_name FROM suite")
141 suite_ql = q.getresult()
145 suite_ids[name] = sid
146 # Build up architecture mapping
147 q = projectB.query("SELECT id, arch_string FROM architecture")
148 for i in q.getresult():
152 # Pre-create the dictionary
153 for suite_id in suites.keys():
155 for arch_id in arches.keys():
156 d[suite_id][arch_id] = 0
157 # Get the raw data for binaries
158 q = projectB.query("""
159 SELECT ba.suite, b.architecture
160 FROM binaries b, bin_associations ba
161 WHERE b.id = ba.bin""")
162 # Simultate 'GROUP by suite, architecture' with a dictionary
163 for i in q.getresult():
164 (suite_id, arch_id) = i
165 d[suite_id][arch_id] = d[suite_id][arch_id] + 1
166 # Get the raw data for source
167 arch_id = arch_ids["source"]
168 q = projectB.query("""
169 SELECT suite, count(suite) FROM src_associations GROUP BY suite;""")
170 for i in q.getresult():
171 (suite_id, count) = i
172 d[suite_id][arch_id] = d[suite_id][arch_id] + count
175 suite_list = suites.values()
176 suite_list.sort(suite_sort)
179 for suite in suite_list:
180 suite_id = suite_ids[suite]
181 suite_arches[suite_id] = {}
182 for arch in Cnf.ValueList("Suite::%s::Architectures" % (suite)):
183 suite_arches[suite_id][arch] = ""
184 suite_id_list.append(suite_id)
185 output_list = [ output_format(i) for i in suite_list ]
186 longest_suite = longest(output_list)
187 arch_list = arches.values()
189 longest_arch = longest(arch_list)
191 output = (" "*longest_arch) + " |"
192 for suite in output_list:
193 output = output + suite.center(longest_suite)+" |"
194 output = output + "\n"+(len(output)*"-")+"\n"
196 arch_list = arches.values()
198 longest_arch = longest(arch_list)
199 for arch in arch_list:
200 arch_id = arch_ids[arch]
201 output = output + arch.center(longest_arch)+" |"
202 for suite_id in suite_id_list:
203 if suite_arches[suite_id].has_key(arch):
204 count = repr(d[suite_id][arch_id])
207 output = output + count.rjust(longest_suite)+" |"
208 output = output + "\n"
211 ################################################################################
216 Cnf = utils.get_conf()
217 Arguments = [('h',"help","Stats::Options::Help")]
219 if not Cnf.has_key("Stats::Options::%s" % (i)):
220 Cnf["Stats::Options::%s" % (i)] = ""
222 args = apt_pkg.ParseCommandLine(Cnf, Arguments, sys.argv)
224 Options = Cnf.SubTree("Stats::Options")
229 utils.warn("dak stats requires a MODE argument")
232 utils.warn("dak stats accepts only one MODE argument")
234 mode = args[0].lower()
236 projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"]))
238 if mode == "arch-space":
240 elif mode == "pkg-nums":
242 elif mode == "daily-install":
243 daily_install_stats()
245 utils.warn("unknown mode '%s'" % (mode))
248 ################################################################################
250 if __name__ == '__main__':