Update static page generation; display locations in alphabetical order in the portal
[hcoop/zz_old/portal.git] / static / gen.sml
CommitLineData
566fd712 1structure Gen :> GEN =
2struct
3
4structure C = PgClient
5
7b8746ef 6structure IntKey = struct
7 type ord_key = int
8 val compare = Int.compare
9end
10
11structure NM = BinaryMapFn(IntKey)
12
cb855527 13val outputDir = "/home/hcoop/public_html/dyn/"
566fd712 14
15fun generate () =
16 let
646dca75 17 val db = C.conn "dbname='hcoop_hcoop'"
566fd712 18
1fe415e0 19 fun procOutput (outf, cmdline) =
20 let
21 val proc = Unix.execute cmdline
22 val ins = Unix.textInstreamOf proc
23
24 fun loop () =
25 case TextIO.inputLine ins of
26 NONE => ()
27 | SOME s => (TextIO.output (outf, s); loop ())
28 in
29 loop ();
30 ignore (Unix.reap proc)
31 end
32
566fd712 33 fun header (outf, title) =
1fe415e0 34 procOutput (outf, ("/usr/local/bin/hcoop_header", [title]))
566fd712 35
36 fun footer outf =
1fe415e0 37 procOutput (outf, ("/usr/local/bin/hcoop_footer", []))
566fd712 38
39 fun genMemberList () =
40 let
41 val outf = TextIO.openOut (outputDir ^ "members.html")
42
43 fun printOne ([name, rname, usr], (total, anon)) =
44 let
45 val name = C.stringFromSql name
46 val rname = C.stringFromSql rname
47 val isAnon = C.isNull usr
48 in
49 if not isAnon then
50 (TextIO.output (outf, "<tr> <td><a href=\"member/");
51 TextIO.output (outf, name);
52 TextIO.output (outf, ".html\">");
53 TextIO.output (outf, name);
54 TextIO.output (outf, "</a></td> <td>");
55 TextIO.output (outf, rname);
56 TextIO.output (outf, "</td> </tr>\n"))
57 else
58 ();
59 (total + 1, (if C.isNull usr then anon + 1 else anon))
60 end
61 | printOne _ = raise Fail "Bad printOne line"
62
1fe415e0 63 val _ = header (outf, "HCoop Member List")
566fd712 64 val _ = TextIO.output (outf, "<table>\n")
65 val (total, anon) = C.fold db printOne (0, 0)
ea455810 66 "SELECT name, rname, usr FROM WebUserPaying LEFT OUTER JOIN DirectoryPref ON id = usr ORDER BY name"
566fd712 67 in
68 TextIO.output (outf, "</table><br><br>\n\nUnlisted members: ");
69 TextIO.output (outf, Int.toString anon);
70 TextIO.output (outf, " out of ");
71 TextIO.output (outf, Int.toString total);
72 TextIO.output (outf, " total.\n");
73 footer outf;
74 TextIO.closeOut outf
75 end
7b8746ef 76
77 fun genMemberPages () =
78 let
79 fun doOne [id, name, rname, joined, usr] =
80 let
81 val id = C.intFromSql id
82 val name = C.stringFromSql name
83 val rname = C.stringFromSql rname
84 val joined = C.timestampFromSql joined
85 val anon = C.isNull usr
86 in
87 if anon then
88 ()
89 else
90 let
91 val outf = TextIO.openOut (String.concat [outputDir, "member/", name, ".html"])
92
1fe415e0 93 val _ = header (outf, "HCoop member: " ^ name)
7b8746ef 94 val _ = TextIO.output (outf, "<table>\n<tr> <td align=\"right\"><b>Member</b>:</td> <td>")
95 val _ = TextIO.output (outf, name)
96 val _ = TextIO.output (outf, "</td> </tr>\n<tr> <td align=\"right\"><b>Name</b>:</td> <td>")
97 val _ = TextIO.output (outf, rname)
98 val _ = TextIO.output (outf, "</td> </tr>\n<tr> <td align=\"right\"><b>Joined</b>:</td> <td>")
99 val _ = TextIO.output (outf, Date.toString (Date.fromTimeLocal joined))
100
101 fun doLocation [id] = C.intFromSql id
102 | doLocation _ = raise Fail "Bad location row"
103
104 val locations = C.map db doLocation (String.concat ["SELECT id FROM Location JOIN Lives ON loc = id AND usr = ",
105 Int.toString id])
106
107 fun printLocation id =
108 let
109 val (name, parent) =
110 case C.oneRow db ("SELECT name, parent FROM Location WHERE id = " ^ Int.toString id) of
111 [name, parent] => (C.stringFromSql name, if C.isNull parent then NONE else SOME (C.intFromSql parent))
112 | _ => raise Fail "Bad printLocation row"
113 in
114 case parent of
115 NONE => ()
116 | SOME parent => (printLocation parent;
117 TextIO.output (outf, " : "));
118 TextIO.output (outf, name)
119 end
120
121 val first = ref true
122
123 fun appLocation id =
124 (if !first then
125 first := false
126 else
127 TextIO.output (outf, ", ");
128 printLocation id)
129
130 val _ = case locations of
131 [] => ()
132 | _ =>
133 (TextIO.output (outf, "<tr> </tr>\n\n<tr> <td align=\"right\"><b>Locations</b>:</td> <td>");
134 app appLocation locations;
135 TextIO.output (outf, "</td> </tr>\n"))
136
137 fun doLink [url, title, descr] = (C.stringFromSql url, C.stringFromSql title, C.stringFromSql descr)
138 | doLink _ = raise Fail "Bad link row"
139
140 val links = C.map db doLink (String.concat ["SELECT url, title, descr FROM Link WHERE usr = ",
141 Int.toString id,
142 " ORDER BY title"])
143
144 fun appLink (url, title, descr) =
145 (TextIO.output (outf, "<tr> <td></td> <td><b><a href=\"");
146 TextIO.output (outf, Web.html url);
147 TextIO.output (outf, "\">");
148 TextIO.output (outf, Web.html title);
149 TextIO.output (outf, "</a></b>");
150 if descr <> "" then
151 TextIO.output (outf, ": ")
152 else
153 ();
154 TextIO.output (outf, Web.html descr);
155 TextIO.output (outf, "</td> </tr>\n"))
156
157 val _ = case links of
158 [] => ()
159 | _ =>
160 (TextIO.output (outf, "<tr> </tr>\n\n<tr> <td><b>Hosted sites</b></td> </tr>\n");
161 app appLink links)
162
163 fun doContact [v, name, url, urlPrefix, urlPostfix] =
164 (C.stringFromSql v, C.stringFromSql name,
165 if C.boolFromSql url then SOME (C.stringFromSql urlPrefix, C.stringFromSql urlPostfix) else NONE)
166 | doContact _ = raise Fail "Bad contact row"
167
168 val contacts = C.map db doContact (String.concat ["SELECT v, name, url, urlPrefix, urlPostfix",
169 " FROM Contact JOIN ContactKind ON knd = ContactKind.id",
170 " WHERE priv = 0",
171 " AND usr = ",
172 Int.toString id,
173 " ORDER BY name, v"])
174
175 fun appContact (v, name, url) =
176 let
177 val link =
178 case url of
179 SOME (pre, post) => String.concat ["<a href=\"", pre, Web.html v, post, "\">", Web.html v, "</a>"]
180 | NONE => v
181 in
182 TextIO.output (outf, "<tr> <td align=\"right\" valign=\"top\"><b>");
183 TextIO.output (outf, Web.html name);
184 TextIO.output (outf, "</b>:</td>\n<td>");
185 TextIO.output (outf, link);
186 TextIO.output (outf, "</td> </tr>\n")
187 end
188
189 val _ = case contacts of
190 [] => ()
191 | _ =>
192 (TextIO.output (outf, "<tr> </tr>\n\n<tr> <td><b>Contact information</b></td> </tr>\n");
193 app appContact contacts)
194 in
195 TextIO.output (outf, "</table>\n");
196 footer outf;
197 TextIO.closeOut outf
198 end
199 end
200 | doOne _ = raise Fail "Bad member row"
201 in
f98251aa 202 ignore (OS.Process.system ("/bin/rm " ^ outputDir ^ "member/*.html"));
ea455810 203 C.app db doOne "SELECT id, name, rname, joined, usr FROM WebUserPaying LEFT OUTER JOIN DirectoryPref ON usr = id"
7b8746ef 204 end
205
206 fun genLocations () =
207 let
208 val outf = TextIO.openOut (outputDir ^ "locs.html")
209
1fe415e0 210 val _ = header (outf, "HCoop: Where members live")
7b8746ef 211
212 fun countResidents () =
213 let
214 fun mkLivesRow [loc, usr] =
215 {loc = C.intFromSql loc, usr = C.intFromSql usr}
216 | mkLivesRow row = raise Fail "Bad lives row"
217
218 fun folder (row, count) =
219 let
220 fun addToParents (id, count) =
221 let
222 val count = NM.insert (count, id, (case NM.find (count, id) of
223 NONE => 1
224 | SOME n => n+1))
225 in
226 case C.oneRow db ("SELECT parent FROM Location WHERE id = " ^ C.intToSql id) of
227 [p] => if C.isNull p then
228 count
229 else
230 addToParents (C.intFromSql p, count)
231 | r => raise Fail "Bad addToParents row"
232 end
233
234 val lives = mkLivesRow row
235 in
236 addToParents (#loc lives, count)
237 end
238 in
cb855527 239 C.fold db folder NM.empty "SELECT loc, usr FROM Lives JOIN WebUserActive ON id = usr"
7b8746ef 240 end
241
242 val res = countResidents ()
243
244 fun locationTree root =
245 let
246 fun doOne [id, name] =
247 let
248 val id = C.intFromSql id
249 val name = C.stringFromSql name
250 in
7b8746ef 251 case NM.find (res, id) of
252 NONE => ()
6ad764f5 253 | SOME n =>
254 (TextIO.output (outf, "<li> ");
255 TextIO.output (outf, Web.html name);
256 TextIO.output (outf, " (");
257 TextIO.output (outf, Int.toString n);
258 TextIO.output (outf, ")");
259 TextIO.output (outf, "</li>\n<ul>\n");
260 locationTree (SOME id);
261 TextIO.output (outf, "</ul>\n"))
7b8746ef 262 end
263 | doOne _ = raise Fail "Bad locationTree row"
264 in
265 C.app db doOne (String.concat ["SELECT id, name FROM Location WHERE parent ",
266 (case root of NONE => "IS NULL" | SOME p => "= " ^ Int.toString p),
267 " ORDER BY name"])
268 end
269 in
270 locationTree NONE;
271 footer outf;
272 TextIO.closeOut outf
273 end
3bc39953 274
275 fun genLinks () =
276 let
277 val outf = TextIO.openOut (outputDir ^ "sites.html")
278
1fe415e0 279 val _ = header (outf, "HCoop: Hosted sites")
3bc39953 280
281 fun doLink [url, title, descr, name] = (C.stringFromSql url, C.stringFromSql title, C.stringFromSql descr, C.stringFromSql name)
282 | doLink _ = raise Fail "Bad link' row"
283
284 val links = C.map db doLink (String.concat ["SELECT url, title, descr, name FROM Link",
ea455810 285 " JOIN WebUserPaying ON WebUserPaying.id = usr",
286 " JOIN DirectoryPref ON WebUserPaying.id = DirectoryPref.usr",
3bc39953 287 " ORDER BY title"])
288
289 fun appLink (url, title, descr, name) =
290 (TextIO.output (outf, "<tr> <td><b><a href=\"");
291 TextIO.output (outf, Web.html url);
292 TextIO.output (outf, "\">");
293 TextIO.output (outf, Web.html title);
294 TextIO.output (outf, "</a></b></td> <td>");
295 TextIO.output (outf, Web.html descr);
296 TextIO.output (outf, "</td> <td><a href=\"member/");
297 TextIO.output (outf, name);
298 TextIO.output (outf, ".html\">");
299 TextIO.output (outf, name);
300 TextIO.output (outf, "</a></td> </tr>\n"))
301 in
302 TextIO.output (outf, "<table>\n");
303 app appLink links;
304 footer outf;
305 TextIO.closeOut outf
306 end
566fd712 307 in
308 genMemberList ();
7b8746ef 309 genMemberPages ();
310 genLocations ();
3bc39953 311 genLinks ();
566fd712 312 OS.Process.success
313 end
314
315fun main _ = (generate ())
7b8746ef 316 handle
317 C.Sql s => (print "SQL exception: \n";
318 print s;
319 OS.Process.failure)
320 | Fail s => (print "Fail: ";
321 print s;
322 OS.Process.failure)
323 | ex => (print "Exception!\n";
324 List.app (fn s => print (s ^ "\n")) (SMLofNJ.exnHistory ex);
566fd712 325 OS.Process.failure)
326
646dca75 327end