Refactor webbw
[hcoop/zz_old/domtool.git] / src / apache / stats / webbw.sml
CommitLineData
42a86d96
AC
1(*
2Domtool (http://hcoop.sf.net/)
3Copyright (C) 2004-2005 Adam Chlipala
4
5This program is free software; you can redistribute it and/or
6modify it under the terms of the GNU General Public License
7as published by the Free Software Foundation; either version 2
8of the License, or (at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18*)
19
20(* Generation of aggregate per-user/per-vhost web bandwidth statistics *)
21
22open Config
23open ApacheConfig
4471dd55 24open Util
42a86d96 25
36f5ca38
AC
26val groupsBase = wblDocDir ^ "/" ^ defaultHost ^ "/url_"
27(* Where to look for grouped user statistics *)
28
42a86d96
AC
29fun mots m =
30 let
31 open Date
32 in
33 case m of
34 Jan => "Jan"
35 | Feb => "Feb"
36 | Mar => "Mar"
37 | Apr => "Apr"
38 | May => "May"
39 | Jun => "Jun"
40 | Jul => "Jul"
41 | Aug => "Aug"
42 | Sep => "Sep"
43 | Oct => "Oct"
44 | Nov => "Nov"
45 | Dec => "Dec"
46 end
47
48fun motn m =
49 let
50 open Date
51 in
52 case m of
53 Jan => "01"
54 | Feb => "02"
55 | Mar => "03"
56 | Apr => "04"
57 | May => "05"
58 | Jun => "06"
59 | Jul => "07"
60 | Aug => "08"
61 | Sep => "09"
62 | Oct => "10"
63 | Nov => "11"
64 | Dec => "12"
65 end
66
67val monthInc = Time.fromSeconds (LargeInt.fromInt 2592000)
68
69fun doit () =
70 let
71 val inf = TextIO.openIn nogroups
72 fun readNogr L =
73 case TextIO.inputLine inf of
74 NONE => L
75 | SOME s => readNogr ((hd (String.tokens Char.isSpace s)) :: L)
76 val nogr = readNogr []
77 val _ = TextIO.closeIn inf
78
79 val now = Date.fromTimeLocal (Time.now ())
80
81 val dir = Posix.FileSys.opendir wblDocDir
82
83 fun backupMonth t =
84 let
85 val now = Date.fromTimeLocal t
86
87 fun backupMonth' t =
88 let
89 val d = Date.fromTimeLocal t
90 in
91 if Date.month d = Date.month now then
92 backupMonth' (Time.- (t, monthInc))
93 else
94 t
95 end
96 in
97 backupMonth' t
98 end
99
100 fun backupMulti n =
101 if n = 0 then
102 Time.now ()
103 else
104 backupMonth (backupMulti (n-1))
105
106 val now =
107 case CommandLine.arguments () of
108 [n] =>
109 (case Int.fromString n of
110 NONE => raise Fail "Invalid integer parameter"
111 | SOME n =>
112 if n >= 0 then
113 Date.fromTimeLocal (backupMulti n)
114 else
115 raise Fail "Negative parameter")
116 | _ => now
117
118 val when = mots (Date.month now) ^ " " ^ Int.toString (Date.year now)
119
120 val groups = let
121 val inf = TextIO.openIn (groupsBase ^ Int.toString (Date.year now) ^ motn (Date.month now) ^ ".html")
122 val _ = TextIO.inputLine inf
123 val _ = TextIO.inputLine inf
124 val _ = TextIO.inputLine inf
125 val _ = TextIO.inputLine inf
126
127 fun loop groups =
128 case TextIO.inputLine inf of
129 NONE => groups
130 | SOME line =>
131 case String.tokens Char.isSpace line of
132 [hits, perc, kb, kbperc, url] =>
133 if size url >= 4
134 andalso String.sub (url, 0) = #"/"
135 andalso String.sub (url, 1) = #"~"
136 andalso String.sub (url, size url - 2) = #"/"
137 andalso String.sub (url, size url - 1) = #"*" then
138 let
139 val uname = String.substring (url, 2, size url - 4)
140 in
141 loop (((uname, ["www.hcoop.net"]),
142 valOf (Int.fromString kb)) :: groups)
143 end
144 else
145 loop groups
146 | _ => groups
147
148 val groups : ((string * string list) * int) list ref = ref (loop [])
149 val _ = TextIO.closeIn inf
150 in
151 groups
152 end handle ex => ref []
153
154 fun addGroup (group, n, d) =
155 if List.exists (fn x => x = d) nogr then
156 ()
157 else let
158 val groups' = if List.exists (fn ((x, _), _) => x = group) (!groups) then
159 map (fn v as ((gr, ds), n') => if gr = group then ((gr, d :: ds), n + n') else v) (!groups)
160 else
161 ((group, [d]), n) :: (!groups)
162 in
163 groups := groups'
164 end
165
166 fun dodir d =
167 let
168 val inf = TextIO.openIn (wblDocDir ^ "/" ^ d ^ "/index.html")
169
170 fun andWeep () =
171 let
172 fun waste n =
173 if n <= 0 then
174 ()
175 else
176 (TextIO.inputLine inf;
177 waste (n-1))
178
179 val _ = waste 5
180
181 val l = valOf (TextIO.inputLine inf)
182 val num = String.extract (l, 32, NONE)
183
184 fun getNum i =
185 if Char.isDigit (String.sub (num, i)) then
186 getNum (i+1)
187 else
188 valOf (Int.fromString (String.substring (num, 0, i)))
189 in
190 getNum 0
191 end
192
193 fun readEm () =
194 case TextIO.inputLine inf of
195 NONE => NONE
196 | SOME l =>
197 if Substring.isSubstring when (Substring.full l) then
198 SOME (andWeep ())
199 else
200 readEm ()
201
202 val ret = readEm ()
203
204 val group =
205 if d <> defaultHost then
206 let
207 val domdir = "/etc/domains/" ^ String.concatWith "/" (rev (String.tokens (fn ch => ch = #".") d))
208 val stat = Posix.FileSys.stat domdir
209 in
210 SOME (Posix.SysDB.Group.name (Posix.SysDB.getgrgid (Posix.FileSys.ST.gid stat)))
211 end
212 else
213 NONE
214 in
215 (case (ret, group) of
216 (SOME ret, SOME group) => addGroup (group, ret, d)
217 | _ => ());
218 TextIO.closeIn inf;
219 ret
220 end handle IO => NONE
221
222 fun loop L =
223 case Posix.FileSys.readdir dir of
224 NONE => L
225 | SOME d => (case dodir d of
226 NONE => loop L
227 | SOME n => loop ((d, n) :: L))
228
229 val doms = loop []
230 val doms = mergeSort doms
231 val groups = mergeSort (!groups)
232
233 val sum = List.foldl (fn ((_, n), s) => s+n) 0 doms
234 in
235 print ("TOTAL: " ^ Int.toString sum ^ "\n\n");
236 List.app (fn (d, n) => print (d ^ ": " ^ Int.toString n ^ "\n")) doms;
237 print "\n";
238 List.app (fn ((d, ds), n) => print (d ^ "[" ^ String.concatWith "," ds ^ "]: " ^ Int.toString n ^ "\n")) groups;
239 Posix.FileSys.closedir dir
240 end
241
242val _ = doit ()