summaryrefslogtreecommitdiffstats
path: root/Tools/webchecker/wcgui.py
blob: 745ee7f91b676a0b7067c3c75c862ed9fdbd79ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
#! /usr/bin/env python

"""GUI interface to webchecker.

This works as a Grail applet too!  E.g.

  <APPLET CODE=wcgui.py NAME=CheckerWindow>
  <PARAM NAME=roots VALUE="http://<yourfavoritehost>">
  </APPLET>

Checkpoints are not (yet?) supported.

User interface:

When the 'Go' checkbutton is checked, web pages will be checked, one
at a time in the Tk 'idle' time.  The other checkbuttons determine
whether the corresponding output panel is shown.  There are six
panels:

Log        -- raw output from the checker (-v, -q affect this)
To check   -- local links discovered but not yet checked
Off site   -- links discovered that point off site
Checked    -- local links that have been checked
Bad links  -- links that failed upon checking
Details    -- details about one URL; double click on a URL in any of
              the aboce list panels (not in Log) will show that URL

XXX There ought to be a list of pages known to contain at least one
bad link.

Use your window manager's Close command to quit.

Command line options:

-m bytes  -- skip HTML pages larger than this size (default %(MAXPAGE)d)
-n        -- reports only, no checking (use with -R)
-q        -- quiet operation (also suppresses external links report)
-v        -- verbose operation; repeating -v will increase verbosity
-x        -- check external links (during report phase)

Command line arguments:

rooturl   -- URL to start checking
             (default %(DEFROOT)s)

XXX The command line options should all be GUI accessible.

XXX The roots should be a list and there should be a GUI way to add a
root.

XXX The alphabetizing of the lists makes the display very jumpy.

XXX The multipanel user interface is bogus.

"""

# ' Emacs bait


import sys
import getopt
import string
from Tkinter import *
import tktools
import webchecker
import random


def main():
    checkext = 0
    try:
	opts, args = getopt.getopt(sys.argv[1:], 'm:qvx')
    except getopt.error, msg:
	sys.stdout = sys.stderr
	print msg
	print __doc__%vars(webchecker)
	sys.exit(2)
    for o, a in opts:
	if o == '-m':
	    webchecker.maxpage = string.atoi(a)
	if o == '-q':
	    webchecker.verbose = 0
	if o == '-v':
	    webchecker.verbose = webchecker.verbose + 1
	if o == '-x':
	    checkext = 1
    root = Tk(className='Webchecker')
    root.protocol("WM_DELETE_WINDOW", root.quit)
    c = CheckerWindow(root, checkext)
    for arg in args or [webchecker.DEFROOT]:
	c.addroot(arg)
    root.mainloop()


class CheckerWindow(webchecker.Checker):

    def __init__(self, parent, checkext=0, roots=None):
	self.__parent = parent
	self.__checkext = checkext
	self.__controls = Frame(parent)
	self.__controls.pack(side=TOP, fill=X)
	self.__govar = IntVar()
	self.__go = Checkbutton(self.__controls, text="GO",
				variable=self.__govar,
				command=self.go)
	self.__go.pack(side=LEFT)
	self.__status = Label(parent, text="Status: initial", anchor=W)
	self.__status.pack(side=TOP, fill=X)
	self.__checking = Label(parent, text="Checking: none", anchor=W)
	self.__checking.pack(side=TOP, fill=X)
	self.__mp = mp = MultiPanel(parent)
	sys.stdout = self.__log = LogPanel(mp, "Log")
	self.__todo = ListPanel(mp, "To check", self.showinfo)
	self.__ext = ListPanel(mp, "Off site", self.showinfo)
	self.__done = ListPanel(mp, "Checked", self.showinfo)
	self.__bad = ListPanel(mp, "Bad links", self.showinfo)
	self.__details = LogPanel(mp, "Details")
	self.__extodo = []
	webchecker.Checker.__init__(self)
	if roots:
	    roots = string.split(roots)
	    for root in roots:
		self.addroot(root)

    def go(self):
	if self.__govar.get():
	    self.__parent.after_idle(self.dosomething)
	else:
	    self.__checking.config(text="Checking: none")

    def dosomething(self):
	if self.todo:
	    i = random.randint(0, len(self.todo)-1)
	    url = self.__todo.items[i]
	    self.__checking.config(text="Checking: "+url)
	    self.__parent.update()
	    self.dopage(url)
	elif self.__checkext and self.__extodo:
	    # XXX Should have an indication of these in the todo window...
	    i = random.randint(0, len(self.__extodo)-1)
	    url = self.__extodo[i]
	    del self.__extodo[i]
	    self.__checking.config(text="Checking: "+url)
	    self.__parent.update()
	    self.checkextpage(url)
	else:
	    self.__govar.set(0)
	self.go()

    def showinfo(self, url):
	d = self.__details
	d.clear()
	d.write("URL: %s\n" % url)
	if self.bad.has_key(url):
	    d.write("Error: %s\n" % str(self.bad[url]))
	if self.done.has_key(url):
	    d.write("Status: done\n")
	    o = self.done[url]
	elif self.todo.has_key(url):
	    d.write("Status: todo\n")
	    o = self.todo[url]
	elif self.ext.has_key(url):
	    d.write("Status: ext\n")
	    o = self.ext[url]
	else:
	    d.write("Status: unknown (!)\n")
	    o = []
	if url in self.roots:
	    d.write("This is a root URL\n")
	for source, rawlink in o:
	    d.write("Origin: %s" % source)
	    if rawlink != url:
		d.write(" (%s)" % rawlink)
	    d.write("\n")
	self.__mp.showpanel("Details")

    def newstatus(self):
	self.__status.config(text="Status: "+self.status()[1:-1])
	self.__parent.update()

    def setbad(self, url, msg):
	webchecker.Checker.setbad(self, url, msg)
	self.__bad.insert(url)
	self.newstatus()

    def setgood(self, url):
	webchecker.Checker.setgood(self, url)
	self.__bad.remove(url)
	self.newstatus()

    def newextlink(self, url, origin):
	webchecker.Checker.newextlink(self, url, origin)
	self.__extodo.append(url)
	self.__ext.insert(url)
	self.newstatus()

    def newintlink(self, url, origin):
	webchecker.Checker.newintlink(self, url, origin)
	if self.done.has_key(url):
	    self.__done.insert(url)
	elif self.todo.has_key(url):
	    self.__todo.insert(url)
	self.newstatus()

    def markdone(self, url):
	webchecker.Checker.markdone(self, url)
	self.__done.insert(url)
	self.__todo.remove(url)
	self.newstatus()


class ListPanel:

    def __init__(self, mp, name, showinfo=None):
	self.mp = mp
	self.name = name
	self.showinfo = showinfo
	self.panel = mp.addpanel(name)
	self.list, self.frame = tktools.make_list_box(
	    self.panel, width=60, height=5)
	self.list.config(exportselection=0)
	if showinfo:
	    self.list.bind('<Double-Button-1>', self.event)
	self.items = []

    def event(self, dummy):
	l = self.list.curselection()
	if not l: return
	self.showinfo(self.list.get(string.atoi(l[0])))

    def insert(self, url):
	if url not in self.items:
	    if not self.items:
		self.mp.showpanel(self.name)
	    # (I tried sorting alphabetically, but the display is too jumpy)
	    i = len(self.items)
	    self.list.insert(i, url)
	    self.list.select_clear(0, END)
	    self.list.select_set(i)
	    self.list.yview(i)
	    self.items.insert(i, url)

    def remove(self, url):
	try:
	    i = self.items.index(url)
	except (ValueError, IndexError):
	    pass
	else:
	    self.list.delete(i)
	    self.list.select_clear(i)
	    del self.items[i]
	    if not self.items:
		self.mp.hidepanel(self.name)


class LogPanel:

    def __init__(self, mp, name):
	self.mp = mp
	self.name = name
	self.panel = mp.addpanel(name)
	self.text, self.frame = tktools.make_text_box(self.panel, height=10)
	self.text.config(wrap=NONE)

    def clear(self):
	self.text.delete("1.0", END)
	self.text.yview("1.0")

    def write(self, s):
	self.text.insert(END, s)
	self.text.yview(END)
	self.panel.update()


class MultiPanel:

    def __init__(self, parent):
	self.parent = parent
	self.frame = Frame(self.parent)
	self.frame.pack(expand=1, fill=BOTH)
	self.topframe = Frame(self.frame, borderwidth=2, relief=RAISED)
	self.topframe.pack(fill=X)
	self.botframe = Frame(self.frame)
	self.botframe.pack(expand=1, fill=BOTH)
	self.panelnames = []
	self.panels = {}

    def addpanel(self, name, on=0):
	v = StringVar()
	if on:
	    v.set(name)
	else:
	    v.set("")
	check = Checkbutton(self.topframe, text=name,
			    offvalue="", onvalue=name, variable=v,
			    command=self.checkpanel)
	check.pack(side=LEFT)
	panel = Frame(self.botframe)
	label = Label(panel, text=name, borderwidth=2, relief=RAISED, anchor=W)
	label.pack(side=TOP, fill=X)
	t = v, check, panel
	self.panelnames.append(name)
	self.panels[name] = t
	if on:
	    panel.pack(expand=1, fill=BOTH)
	return panel

    def showpanel(self, name):
	v, check, panel = self.panels[name]
	v.set(name)
	panel.pack(expand=1, fill=BOTH)

    def hidepanel(self, name):
	v, check, panel = self.panels[name]
	v.set("")
	panel.pack_forget()

    def checkpanel(self):
	for name in self.panelnames:
	    v, check, panel = self.panels[name]
	    panel.pack_forget()
	for name in self.panelnames:
	    v, check, panel = self.panels[name]
	    if v.get():
		panel.pack(expand=1, fill=BOTH)


if __name__ == '__main__':
    main()