~divmod-dev/divmod.org/imap-server-440

« back to all changes in this revision

Viewing changes to Reverend/reverend/ui/trainer.py

  • Committer: washort
  • Date: 2005-10-25 18:49:27 UTC
  • Revision ID: svn-v4:866e43f7-fbfc-0310-8f2a-ec88d1da2979:trunk:2573
import Reverend

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# This module is part of the Divmod project and is Copyright 2003 Amir Bakhtiar:
 
2
# amir@divmod.org.  This is free software; you can redistribute it and/or
 
3
# modify it under the terms of version 2.1 of the GNU Lesser General Public
 
4
# License as published by the Free Software Foundation.
 
5
#
 
6
 
 
7
from Tkinter import *
 
8
import tkFileDialog
 
9
import tkSimpleDialog
 
10
import tkMessageBox
 
11
 
 
12
import os
 
13
 
 
14
from util import Command, StatusBar, Notebook
 
15
from tester import TestView
 
16
 
 
17
class PoolView(Frame):
 
18
    def __init__(self, master=None, guesser=None, app=None):
 
19
        Frame.__init__(self, master, bg='lightblue3')
 
20
        self.pack()
 
21
        self.listView = Frame(self)
 
22
        self.listView.pack()
 
23
        bp = Button(self, text="New Pool", command=self.newPool)
 
24
        bp.pack(side=LEFT, anchor=SE)
 
25
        self.addLoadSave()
 
26
        self.columnHeadings()
 
27
        self.model = {}
 
28
        self.guesser = guesser
 
29
        self.app = app
 
30
        self.reload()
 
31
 
 
32
    def reload(self):
 
33
        self.listView.destroy()
 
34
        self.listView = Frame(self)
 
35
        self.listView.pack()
 
36
        for pool in self.guesser.poolNames():
 
37
            self.addPool(self.guesser.pools[pool])
 
38
        self.addPool(self.guesser.corpus, 'Total')
 
39
 
 
40
    def upload(self):
 
41
        pass
 
42
    
 
43
    def addLoadSave(self):
 
44
        frame = Frame(self)
 
45
        frame.pack(side=RIGHT)
 
46
        bp = Button(frame, text="Upload", command=self.upload, state=DISABLED)
 
47
        bp.pack(side=BOTTOM, fill=X)
 
48
        bp = Button(frame, text="Save", command=self.save)
 
49
        bp.pack(side=BOTTOM, fill=X)
 
50
        bp = Button(frame, text="Load", command=self.load)
 
51
        bp.pack(side=BOTTOM, fill=X)
 
52
    
 
53
    def addPool(self, pool, name=None):
 
54
        col=None
 
55
        tTok = IntVar()
 
56
        train = IntVar()
 
57
        line = Frame(self.listView)
 
58
        line.pack()
 
59
        if name is None:
 
60
            name = pool.name
 
61
            idx = self.guesser.poolNames().index(name)
 
62
            col = self.defaultColours()[idx]
 
63
        l = Label(line, text=name, anchor=W, width=10)
 
64
        l.grid(row=0, column=0)
 
65
        colourStripe = Label(line, text=' ', width=1, bg=col, anchor=W, relief=GROOVE)
 
66
        colourStripe.grid(row=0, column=1)
 
67
        train = IntVar()
 
68
        train.set(pool.trainCount)
 
69
        l = Label(line, textvariable=train, anchor=E, width=10, relief=SUNKEN)
 
70
        l.grid(row=0, column=2)
 
71
        uTok = IntVar()
 
72
        uTok.set(len(pool))
 
73
        l = Label(line, textvariable=uTok, anchor=E, width=12, relief=SUNKEN)
 
74
        l.grid(row=0, column=3)
 
75
        tTok = IntVar()
 
76
        tTok.set(pool.tokenCount)
 
77
        l = Label(line, textvariable=tTok, anchor=E, width=10, relief=SUNKEN)
 
78
        l.grid(row=0, column=4)
 
79
        self.model[name]=(pool, uTok, tTok, train)
 
80
 
 
81
    def refresh(self):
 
82
        for pool, ut, tt, train in self.model.values():
 
83
            ut.set(len(pool))
 
84
            tt.set(pool.tokenCount)
 
85
            train.set(pool.trainCount)
 
86
 
 
87
    def save(self):
 
88
        path = tkFileDialog.asksaveasfilename()
 
89
        if not path:
 
90
            return
 
91
        self.guesser.save(path)
 
92
        self.app.dirty = False
 
93
 
 
94
    def load(self):
 
95
        path = tkFileDialog.askopenfilename()
 
96
        if not path:
 
97
            return
 
98
        self.guesser.load(path)
 
99
        self.reload()
 
100
        self.app.dirty = False
 
101
    
 
102
    def newPool(self):
 
103
        p = tkSimpleDialog.askstring('Create Pool', 'Name for new pool?')
 
104
        if not p:
 
105
            return
 
106
        if p in self.guesser.pools:
 
107
            tkMessageBox.showwarning('Bad pool name!', 'Pool %s already exists.' % p)
 
108
        self.guesser.newPool(p)
 
109
        self.reload()
 
110
        self.app.poolAdded()
 
111
        self.app.status.log('New pool created: %s.' % p, clear=3)
 
112
 
 
113
    def defaultColours(self):
 
114
        return ['green', 'yellow', 'lightblue', 'red', 'blue', 'orange', 'purple', 'pink']
 
115
 
 
116
    def columnHeadings(self):
 
117
        # FIXME factor out and generalize
 
118
        title = Label(self, text='Pools', relief=RAISED, borderwidth=1)
 
119
        title.pack(side=TOP, fill=X)
 
120
        msgLine = Frame(self, relief=RAISED, borderwidth=1)
 
121
        msgLine.pack(side=TOP)
 
122
        currCol = 0
 
123
        colHeadings = [('Name', 10), ('', 1), ('Trained', 10), ('Unique Tokens', 12), ('Tokens', 10)]
 
124
        for cHdr, width in colHeadings:
 
125
            l = Label(msgLine, text=cHdr, width=width, bg='lightblue')
 
126
            l.grid(row=0, column=currCol)
 
127
            currCol += 1
 
128
 
 
129
            
 
130
class Trainer(Frame):
 
131
    def __init__(self, parent, guesser=None, itemClass=None):
 
132
        self.status = StatusBar(parent)
 
133
        self.status.pack(side=BOTTOM, fill=X)
 
134
        Frame.__init__(self, parent)
 
135
        self.pack(side=TOP, fill=BOTH)
 
136
        self.itemsPerPage = 20
 
137
        self.rows = []
 
138
        for i in range(self.itemsPerPage):
 
139
            self.rows.append(ItemRow())
 
140
        self.items = []
 
141
        self.files = []
 
142
        self.cursor = 0
 
143
        self.dirty = False
 
144
        if guesser is None:
 
145
            from reverend.thomas import Bayes
 
146
            self.guesser = Bayes()
 
147
        else:
 
148
            self.guesser = guesser
 
149
        if itemClass is None:
 
150
            self.itemClass = TextItem
 
151
        else:
 
152
            self.itemClass = itemClass
 
153
        for row in self.rows:
 
154
            row.summary.set('foo')
 
155
        self.initViews()
 
156
 
 
157
    def initViews(self):
 
158
        self.nb = Notebook(self)
 
159
##        frame1 = Frame(self.nb())
 
160
##        self.poolView = PoolView(frame1, guesser=self.guesser, app=self)
 
161
##        self.poolView.pack(side=TOP)
 
162
        frame2 = Frame(self.nb())
 
163
        self.poolView = PoolView(frame2, guesser=self.guesser, app=self)
 
164
        self.poolView.pack(side=TOP)
 
165
        self.listView = Canvas(frame2, relief=GROOVE)
 
166
        self.listView.pack(padx=3)
 
167
        bn = Button(self.listView, text="Load training", command=self.loadCorpus)
 
168
        bn.pack(side=RIGHT, anchor=NE, fill=X)
 
169
        self.columnHeadings()
 
170
        self.addNextPrev()
 
171
        
 
172
        frame3 = Frame(self.nb())
 
173
        self.testView = TestView(frame3, guesser=self.guesser, app=self)
 
174
        self.testView.pack()
 
175
 
 
176
        frame4 = Frame(self.nb())
 
177
        bp = Button(frame4, text="Quit", command=self.quitNow)
 
178
        bp.pack(side=BOTTOM)
 
179
        
 
180
        #self.nb.add_screen(frame1, 'Reverend')
 
181
        self.nb.add_screen(frame2, 'Training')
 
182
        self.nb.add_screen(frame3, 'Testing')
 
183
        self.nb.add_screen(frame4, 'Quit')
 
184
        
 
185
 
 
186
    def addNextPrev(self):
 
187
        npFrame = Frame(self.listView)
 
188
        npFrame.pack(side=BOTTOM, fill=X)
 
189
        bn = Button(npFrame, text="Prev Page", command=self.prevPage)
 
190
        bn.grid(row=0, column=0)
 
191
        bn = Button(npFrame, text="Next Page", command=self.nextPage)
 
192
        bn.grid(row=0, column=1)
 
193
 
 
194
 
 
195
    def loadCorpus(self):
 
196
        path = tkFileDialog.askdirectory()
 
197
        if not path:
 
198
            return
 
199
        self.loadFileList(path)
 
200
        self.displayItems()
 
201
        self.displayRows()
 
202
 
 
203
    def bulkTest(self):
 
204
        dirs = []
 
205
        for pool in self.guesser.poolNames():
 
206
            path = tkFileDialog.askdirectory()
 
207
            dirs.append((pool, path))
 
208
        for pool, path in dirs:
 
209
            print pool, path
 
210
            
 
211
 
 
212
    def displayList(self):
 
213
        for item in self.items:
 
214
            self.itemRow(item)
 
215
            
 
216
    def displayRows(self):
 
217
        for row in self.rows:
 
218
            self.displayRow(row)
 
219
 
 
220
    def loadFileList(self, path):
 
221
        listing = os.listdir(path)
 
222
        self.files = [os.path.join(path, file) for file in listing]
 
223
        self.cursor = 0
 
224
 
 
225
    def prevPage(self):
 
226
        self.cursor = max(0, self.cursor - self.itemsPerPage)
 
227
        self.displayItems()
 
228
 
 
229
    def nextPage(self):
 
230
        self.cursor = min(len(self.files), self.cursor + self.itemsPerPage)
 
231
        self.displayItems()
 
232
        
 
233
    def displayItems(self):
 
234
        theseFiles = self.files[self.cursor:self.cursor + self.itemsPerPage]
 
235
        items = []
 
236
        for file, row in zip(theseFiles, self.rows):
 
237
            fp = open(file, 'rb')
 
238
            try:
 
239
                item = self.itemClass.fromFile(fp)
 
240
            finally:
 
241
                fp.close()
 
242
            if item is None:
 
243
                continue
 
244
            items.append(item)
 
245
            guesses = self.guesser.guess(item)
 
246
            summary = item.summary()
 
247
            cols = item.columnDefs()
 
248
            s = ''
 
249
            for c, ignore in cols:
 
250
                s += summary[c] + ' '
 
251
            row.initialize(item, s, guesses, self.guesser.poolNames())
 
252
        self.items = items
 
253
        
 
254
    def quitNow(self):
 
255
        if self.dirty:
 
256
            if tkMessageBox.askyesno("You have unsaved changes!", "Quit without saving?"):
 
257
                self.quit()
 
258
        self.quit()
 
259
 
 
260
    def columnHeadings(self):
 
261
        # FIXME - Something better for columns and rows in general
 
262
        line = Frame(self.listView, relief=RAISED, borderwidth=1)
 
263
        line.pack(side=TOP, padx=2, pady=1)
 
264
        colHeadings = self.itemClass.columnDefs()
 
265
        currCol = 0
 
266
        for cHdr, width in colHeadings:
 
267
            l = Label(line, text=cHdr, width=width, bg='lightblue')
 
268
            l.grid(row=0, column=currCol)
 
269
            currCol += 1
 
270
        line = Frame(self)
 
271
        line.pack(fill=X)
 
272
 
 
273
    def training(self, row):
 
274
        sel = row.selection.get()
 
275
        self.guesser.train(sel, row.original)
 
276
        row.current = sel
 
277
        self.guessAll()
 
278
 
 
279
    def guessAll(self):
 
280
        self.poolView.refresh()
 
281
        pools = self.guesser.poolNames()
 
282
        for row in self.rows:
 
283
            row.setGuess(self.guesser.guess(row.original), pools)
 
284
            
 
285
    def displayRow(self, row, bgc=None):
 
286
        # UGH - REWRITE!
 
287
        line = Frame(self.listView, bg=bgc)
 
288
        line.pack(pady=1)
 
289
        row.line = line
 
290
        self.insertRadios(row)
 
291
        Label(line, text=row.summary.get(), textvariable=row.summary, width=60, bg=bgc,
 
292
              anchor=W).grid(row=0, column=2)
 
293
        #Label(line, text=row.guess, width=7, bg=bgc, anchor=W).grid(row=0, column=1)
 
294
        colourStripe = Label(line, text=' ', width=1, bg=bgc, anchor=W, relief=GROOVE)
 
295
        colourStripe.grid(row=0, column=1)
 
296
        line.colourStripe = colourStripe
 
297
        pools = self.guesser.poolNames()
 
298
        row.refreshColour(pools)
 
299
 
 
300
    def poolAdded(self):
 
301
        if not self.items:
 
302
            return
 
303
        pools = self.guesser.poolNames()
 
304
        for row in self.rows:
 
305
            for r in row.radios:
 
306
                r.destroy()
 
307
            self.insertRadios(row)
 
308
            row.refreshColour(pools)
 
309
        self.dirty = True
 
310
 
 
311
    def insertRadios(self, row):
 
312
        radioFrame = Frame(row.line)
 
313
        radioFrame.grid(row=0, column=0)
 
314
        currCol = 0
 
315
        radios = []
 
316
        v = row.selection
 
317
        ci = 0
 
318
        colours = row.defaultColours()
 
319
        pools = self.guesser.poolNames()
 
320
        for pool in pools:
 
321
            rb = Radiobutton(radioFrame, text=pool, variable=v, value=pool, command=Command(self.training, row), bg=None)
 
322
            rb.grid(row=0, column=currCol)
 
323
            radios.append(rb)
 
324
            currCol += 1
 
325
            ci += 1
 
326
        row.radios = radios
 
327
        
 
328
 
 
329
class TextItem(object):
 
330
    def __init__(self, text):
 
331
        self.text = text
 
332
        
 
333
    def summary(self):
 
334
        return {'Text': self.text}
 
335
 
 
336
    def columnNames(self):
 
337
        return ['Text']
 
338
 
 
339
    def lower(self):
 
340
        return self.text.lower()
 
341
 
 
342
    def fromFile(self, fp):
 
343
        """Return the first line of the file.
 
344
        """
 
345
        ti = self(fp.readline())
 
346
        return ti
 
347
    fromFile = classmethod(fromFile)
 
348
 
 
349
 
 
350
class ItemRow(object):
 
351
    def __init__(self, orig=None):
 
352
        self.line = None
 
353
        self.radios = []
 
354
        self.original = orig
 
355
        self.current = ''
 
356
        self.guess = []
 
357
        self.summary = StringVar()
 
358
        self.selection = StringVar()
 
359
 
 
360
    def initialize(self, item=None, summary='', guess=None, pools=[]):
 
361
        self.selection.set('')
 
362
        self.original = item
 
363
        self.summary.set(summary)
 
364
        self.setGuess(guess, pools)
 
365
 
 
366
    def setGuess(self, guess, pools):
 
367
        if not guess:
 
368
            guess = [['']]
 
369
        self.guess = guess
 
370
        self.selection.set(self.bestGuess())
 
371
        self.current = self.bestGuess()
 
372
        self.refreshColour(pools)
 
373
 
 
374
    def refreshColour(self, pools):
 
375
        col = None
 
376
        if self.guess[0][0] in pools:
 
377
            idx = pools.index(self.guess[0][0])
 
378
            col = self.defaultColours()[idx]
 
379
        if self.line:
 
380
            self.line.colourStripe.config(bg=col)
 
381
 
 
382
    def __repr__(self):
 
383
        return self.original.__repr__()
 
384
 
 
385
    def defaultColours(self):
 
386
        return ['green', 'yellow', 'lightblue', 'red', 'blue', 'orange', 'purple', 'pink']
 
387
 
 
388
    def bestGuess(self):
 
389
        if self.guess:
 
390
            return self.guess[0][0]
 
391
        else:
 
392
            return None
 
393
 
 
394
 
 
395
 
 
396
        
 
397
if __name__ == "__main__":
 
398
    root = Tk()
 
399
    root.title('Reverend Trainer')
 
400
    root.minsize(width=300, height=300)
 
401
    #root.maxsize(width=600, height=600)
 
402
    display = Trainer(root)
 
403
    root.mainloop()