]> git.decadent.org.uk Git - dak.git/blob - dak/stats.py
update2
[dak.git] / dak / stats.py
1 #!/usr/bin/env python
2
3 # Various statistical pr0nography fun and games
4 # Copyright (C) 2000, 2001, 2002, 2003, 2006  James Troup <james@nocrew.org>
5
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.
10
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.
15
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
19
20 ################################################################################
21
22 # <aj>    can we change the standards instead?
23 # <neuro> standards?
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
27 # <aj>    go us!
28
29 # [aj's attempt to avoid ABI changes for released architecture(s)]
30
31 ################################################################################
32
33 import pg, sys
34 import apt_pkg
35 from daklib import utils
36
37 ################################################################################
38
39 Cnf = None
40 projectB = None
41
42 ################################################################################
43
44 def usage(exit_code=0):
45     print """Usage: dak stats MODE
46 Print various stats.
47
48   -h, --help                show this help and exit.
49
50 The following MODEs are available:
51
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
55 """
56     sys.exit(exit_code)
57
58 ################################################################################
59
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""")
66     print q
67     q = projectB.query("SELECT sum(size) FROM files WHERE filename ~ '.(diff.gz|tar.gz|dsc)$'")
68     print q
69
70 ################################################################################
71
72 def daily_install_stats():
73     stats = {}
74     f = utils.open_file("2001-11")
75     for line in f.readlines():
76         split = line.strip().split('|')
77         program = split[1]
78         if program != "katie" and program != "process-accepted":
79             continue
80         action = split[2]
81         if action != "installing changes" and action != "installed":
82             continue
83         date = split[0][:8]
84         if not stats.has_key(date):
85             stats[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])
92
93     dates = stats.keys()
94     dates.sort()
95     for date in dates:
96         packages = stats[date]["packages"]
97         size = int(stats[date]["size"] / 1024.0 / 1024.0)
98         print "%s %s %s" % (date, packages, size)
99
100 ################################################################################
101
102 def longest(list):
103     longest = 0
104     for i in list:
105         l = len(i)
106         if l > longest:
107             longest = l
108     return longest
109
110 def suite_sort(a, b):
111     if Cnf.has_key("Suite::%s::Priority" % (a)):
112         a_priority = int(Cnf["Suite::%s::Priority" % (a)])
113     else:
114         a_priority = 0
115     if Cnf.has_key("Suite::%s::Priority" % (b)):
116         b_priority = int(Cnf["Suite::%s::Priority" % (b)])
117     else:
118         b_priority = 0
119     return cmp(a_priority, b_priority)
120
121 def output_format(suite):
122     output_suite = []
123     for word in suite.split("-"):
124         output_suite.append(word[0])
125     return "-".join(output_suite)
126
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
132
133 def number_of_packages():
134     arches = {}
135     arch_ids = {}
136     suites = {}
137     suite_ids = {}
138     d = {}
139     # Build up suite mapping
140     q = projectB.query("SELECT id, suite_name FROM suite")
141     suite_ql = q.getresult()
142     for i in suite_ql:
143         (sid, name) = i
144         suites[sid] = name
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():
149         (aid, name) = i
150         arches[aid] = name
151         arch_ids[name] = aid
152     # Pre-create the dictionary
153     for suite_id in suites.keys():
154         d[suite_id] = {}
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
173     ## Print the results
174     # Setup
175     suite_list = suites.values()
176     suite_list.sort(suite_sort)
177     suite_id_list = []
178     suite_arches = {}
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()
188     arch_list.sort()
189     longest_arch = longest(arch_list)
190     # Header
191     output = (" "*longest_arch) + " |"
192     for suite in output_list:
193         output = output + suite.center(longest_suite)+" |"
194     output = output + "\n"+(len(output)*"-")+"\n"
195     # per-arch data
196     arch_list = arches.values()
197     arch_list.sort()
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])
205             else:
206                 count = "-"
207             output = output + count.rjust(longest_suite)+" |"
208         output = output + "\n"
209     print output
210
211 ################################################################################
212
213 def main ():
214     global Cnf, projectB
215
216     Cnf = utils.get_conf()
217     Arguments = [('h',"help","Stats::Options::Help")]
218     for i in [ "help" ]:
219         if not Cnf.has_key("Stats::Options::%s" % (i)):
220             Cnf["Stats::Options::%s" % (i)] = ""
221
222     args = apt_pkg.ParseCommandLine(Cnf, Arguments, sys.argv)
223
224     Options = Cnf.SubTree("Stats::Options")
225     if Options["Help"]:
226         usage()
227
228     if len(args) < 1:
229         utils.warn("dak stats requires a MODE argument")
230         usage(1)
231     elif len(args) > 1:
232         utils.warn("dak stats accepts only one MODE argument")
233         usage(1)
234     mode = args[0].lower()
235
236     projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"]))
237
238     if mode == "arch-space":
239         per_arch_space_use()
240     elif mode == "pkg-nums":
241         number_of_packages()
242     elif mode == "daily-install":
243         daily_install_stats()
244     else:
245         utils.warn("unknown mode '%s'" % (mode))
246         usage(1)
247
248 ################################################################################
249
250 if __name__ == '__main__':
251     main()