root / pykoticon / trunk / bin / pykoticon @ 64

Revision 64, 13.7 kB (checked in by jerome, 19 years ago)

Better exit, but not yet there...

  • Property svn:keywords set to Id
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3
4# PyKotIcon - Windows System Tray Icon for PyKota
5#
6# (c) 2003-2004 Jerome Alet <alet@librelogiciel.com>
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20#
21# $Id$
22#
23#
24
25import sys
26import os
27import urllib
28import urllib2
29
30try :
31    import win32api
32    import win32net
33    import win32netcon
34    hasWindowsExtensions = 1
35except ImportError :   
36    hasWindowsExtensions = 0
37    raise ImportError, "Mark Hammond's Win32 Extensions are missing. Please install them."
38   
39try :   
40    import wxPython.wx
41    import wx
42    import wx.grid as gridlib
43    hasWxPython = 1
44except ImportError :   
45    hasWxPython = 0
46    raise ImportError, "wxPython for Windows is missing. Please install it."
47   
48DUMPYKOTA_URL = "http://cgi.librelogiciel.com/cgi-bin/dumpykota.cgi"
49
50class UserQuota :
51    """A class for PyKota User Print Quota entries."""
52    def __init__(self, printername, pagecount, softlimit, hardlimit, datelimit):
53        """Initialize user print quota datas."""
54        self.PrinterName = printername
55        try :
56            self.PageCounter = int(pagecount)
57        except (ValueError, TypeError) :
58            self.PageCounter = 0
59        try :
60            self.SoftLimit = int(softlimit)
61        except (ValueError, TypeError) :
62            self.SoftLimit = None
63        try :
64            self.HardLimit = int(hardlimit)
65        except (ValueError, TypeError) :
66            self.HardLimit = None
67        self.DateLimit = datelimit
68       
69class User :
70    """A class for PyKota users."""
71    def __init__(self, username, userinfo, userquotas) :
72        """Initialize user datas."""
73        self.Name = username
74        self.LimitBy = userinfo["limitby"][0].lower()
75        try :
76            self.Balance = float(userinfo["balance"][0])
77        except (ValueError, TypeError) :
78            self.Balance = 0.0
79        try :
80            self.LifeTimePaid = float(userinfo["lifetimepaid"][0])
81        except (ValueError, TypeError) :
82            self.LifeTimePaid = 0.0
83        self.Quotas = []
84        for i in range(len(userquotas["printername"])) :
85            printername = userquotas["printername"][i]
86            pagecounter = userquotas["pagecounter"][i]
87            softlimit = userquotas["softlimit"][i]
88            hardlimit = userquotas["hardlimit"][i]
89            datelimit = userquotas["datelimit"][i]
90            self.Quotas.append(UserQuota(printername, pagecounter, softlimit, \
91                                         hardlimit, datelimit))
92           
93class CGINetworkInterface :
94    """A class for all network interactions."""
95    def __init__(self, cgiurl, authname=None, authpw=None) :
96        """Initialize CGI connection datas."""
97        self.cgiurl = cgiurl
98        self.authname = authname
99        self.authpw = authpw
100       
101    def retrieveDatas(self, arguments, fieldnames) :
102        """Retrieve datas from the CGI script."""
103        args = { "report" : 1,
104                 "format" : "csv",
105               } 
106        args.update(arguments)           
107        answer = {}
108        try :           
109            url = "%s?%s" % (self.cgiurl, urllib.urlencode(args))
110            u = urllib2.urlopen(url)
111            lines = u.readlines()
112        except IOError, msg :   
113            raise IOError, "Unable to retrieve %s : %s" % (url, msg)
114        else :   
115            u.close()
116            try :
117                lines = [ line.strip().split(",") for line in lines ]
118                fields = [field[1:-1] for field in lines[0]]
119                indices = [fields.index(fname) for fname in fieldnames]
120                answer = dict([ (fieldname, \
121                                  [ line[fields.index(fieldname)][1:-1] \
122                                    for line in lines[1:] ]) \
123                                for fieldname in fieldnames ])
124            except :   
125                raise ValueError, "Invalid datas retrieved from %s" % url
126        return answer
127       
128    def getPrinterNames(self) :
129        """Retrieve the printer's names."""
130        arguments = { "report" : 1,
131                      "format" : "csv",
132                      "datatype" : "printers",
133                    } 
134        return self.retrieveDatas(arguments, ["printername"])["printername"]
135       
136    def getUserInfo(self, username) :
137        """Retrieve the user's information."""
138        arguments = { "datatype" : "users",
139                      "filter" : "username=%s" % username,
140                    } 
141        return self.retrieveDatas(arguments, ("limitby", "balance", \
142                                                         "lifetimepaid"))
143       
144    def getUserPQuotas(self, username) :
145        """Retrieve the user's print quota information."""
146        arguments = { "datatype" : "upquotas",
147                      "filter" : "username=%s" % username,
148                    } 
149        return self.retrieveDatas(arguments, ("printername", "pagecounter", \
150                                              "softlimit", "hardlimit", \
151                                              "datelimit"))
152                                             
153    def getUser(self, username) :
154        """Retrieves the user account and quota information."""
155        info = self.getUserInfo(username)
156        if info :
157            quotas = self.getUserPQuotas(username)
158            return User(username, info, quotas)
159   
160class PyKotIconGrid(gridlib.Grid) :   
161    """A class for user print quota entries."""
162    def __init__(self, parent, quotas) :
163        gridlib.Grid.__init__(self, parent, -1)
164        self.CreateGrid(len(quotas), 4)
165        self.EnableEditing(False)
166        self.SetColLabelValue(0, "Page Counter")
167        self.SetColLabelValue(1, "Soft Limit")
168        self.SetColLabelValue(2, "Hard Limit")
169        self.SetColLabelValue(3, "Date Limit")
170        attr = gridlib.GridCellAttr()
171        attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_RIGHT)
172        attr.SetReadOnly(True)
173        self.SetColAttr(0, attr)
174        self.SetColAttr(1, attr)
175        self.SetColAttr(2, attr)
176        attr = gridlib.GridCellAttr()
177        attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
178        attr.SetReadOnly(True)
179        self.SetColAttr(3, attr)
180        for i in range(len(quotas)) :
181            q = quotas[i]
182            self.SetRowLabelValue(i, quotas[i].PrinterName)
183            self.SetCellValue(i, 0, str(q.PageCounter))
184            self.SetCellValue(i, 1, str(q.SoftLimit))
185            self.SetCellValue(i, 2, str(q.HardLimit))
186            self.SetCellValue(i, 3, str(q.DateLimit))
187            attr = gridlib.GridCellAttr()
188            colour = wx.GREEN
189            if q.SoftLimit is not None :
190                if q.PageCounter >= q.SoftLimit :
191                    colour = wx.RED
192            elif q.HardLimit is not None :       
193                if q.PageCounter >= q.HardLimit :
194                    colour = wx.RED
195            attr.SetBackgroundColour(colour)       
196            self.SetRowAttr(i, attr)
197           
198class PyKotIcon(wxPython.wx.wxFrame):
199    """Main class."""
200    def __init__(self, parent, id):
201        wxPython.wx.wxFrame.__init__(self, parent, -1, \
202               "PyKota Print Quota for user %s" % self.getCurrentUserName(), \
203               # size=(640, 480), \
204               style = wxPython.wx.wxDEFAULT_FRAME_STYLE | wxPython.wx.wxNO_FULL_REPAINT_ON_RESIZE)
205        self.greenicon = wxPython.wx.wxIcon(os.path.join("..", "icons", "pykoticon-green.ico"), \
206                                  wxPython.wx.wxBITMAP_TYPE_ICO)
207        self.redicon = wxPython.wx.wxIcon(os.path.join("..", "icons", "pykoticon-red.ico"), \
208                                  wxPython.wx.wxBITMAP_TYPE_ICO)
209        self.SetIcon(self.greenicon)
210       
211        self.tbicon = wxPython.wx.wxTaskBarIcon()
212        self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
213       
214        wxPython.wx.EVT_TASKBAR_LEFT_DCLICK(self.tbicon, self.OnTaskBarActivate)
215        wxPython.wx.EVT_TASKBAR_RIGHT_UP(self.tbicon, self.OnTaskBarMenu)
216        wxPython.wx.EVT_ICONIZE(self, self.OnIconify)
217       
218        self.TBMENU_RESTORE = wx.NewId()
219        self.TBMENU_CLOSE = wx.NewId()
220        wxPython.wx.EVT_MENU(self.tbicon, self.TBMENU_RESTORE, \
221                                          self.OnTaskBarActivate)
222        wxPython.wx.EVT_MENU(self.tbicon, self.TBMENU_CLOSE, \
223                                          self.OnTaskBarClose)
224        self.menu = wxPython.wx.wxMenu()
225        self.menu.Append(self.TBMENU_RESTORE, "Show Print Quota")
226        self.menu.Append(self.TBMENU_CLOSE, "Quit")
227       
228        wxPython.wx.EVT_CLOSE(self, self.OnClose)
229       
230        self.TBTIMER = wx.NewId()
231        self.chrono = wxPython.wx.wxTimer(self, self.TBTIMER)
232        wxPython.wx.EVT_TIMER(self, self.TBTIMER, self.OnChronoTimer)
233       
234        self.User = None
235        self.networkInterface = CGINetworkInterface(DUMPYKOTA_URL)
236        self.inTimer = False
237        self.chrono.Start(200) # first time in 0.25 second
238
239    def getCurrentUserName(self) :
240        """Returns the name of the current user."""
241        return win32api.GetUserName()
242       
243    def OnChronoTimer(self, event) :
244        """Retrieves user's data quota information."""
245        # we stop it there, needed because we want to
246        # change the delay the first time.
247        self.chrono.Stop()   
248        if self.inTimer is False : # avoids re-entrance
249            self.inTimer = True
250            self.User = self.networkInterface.getUser(self.getCurrentUserName())
251            if self.User.LimitBy == "balance" :
252                if self.User.Balance <= 0.0 :
253                    self.tbicon.SetIcon(self.redicon, "PyKotIcon")
254                else :   
255                    self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
256            else :       
257                isRed = False
258                for q in self.User.Quotas :
259                    if q.SoftLimit is not None :
260                        if q.PageCounter >= q.SoftLimit :
261                            isRed = True
262                            break
263                    elif q.HardLimit is not None :       
264                        if q.PageCounter >= q.HardLimit :
265                            isRed = True
266                            break
267                if isRed is True :
268                    self.tbicon.SetIcon(self.redicon, "PyKotIcon")
269                else :   
270                    self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
271                if not self.IsIconized() :   
272                    self.Iconize(True)
273                if self.IsShown():   
274                    self.Hide()
275                if hasattr(self, "quotasgrid") :   
276                    self.quotasgrid.Destroy()
277                    del self.quotasgrid
278                self.quotasgrid = PyKotIconGrid(self, self.User.Quotas)   
279                if self.IsIconized() :
280                    self.Iconize(False)
281                if not self.IsShown() :
282                    self.Show(True)
283            self.inTimer = False
284        # Now we want it every 3 minutes   
285        #self.chrono.Start(1000 * 60 * 3) # every 3 minutes
286        self.chrono.Start(1000 * 20) # every 20 seconds
287   
288    def OnIconify(self, event) :
289        self.Hide()
290
291    def OnTaskBarActivate(self, event) :
292        if self.IsIconized() :
293            self.Iconize(False)
294        if not self.IsShown() :
295            self.Show(True)
296        self.Raise()
297
298    def OnClose(self, event) :
299        if hasattr(self, "chrono") :
300            self.chrono.Stop()
301            del self.chrono
302        if hasattr(self, "menu") :
303            self.menu.Destroy()
304            del self.menu
305        if hasattr(self, "tbicon") :
306            self.tbicon.Destroy()
307            del self.tbicon
308        self.Destroy()
309
310    def OnTaskBarMenu(self, evt) :
311        self.tbicon.PopupMenu(self.menu)
312
313    def OnTaskBarClose(self, evt) :
314        self.Close()
315        wxPython.wx.wxGetApp().ProcessIdle()
316
317class PyKotIconApp(wxPython.wx.wxApp):
318    def OnInit(self) :
319        try :
320            frame = PyKotIcon(None, -1)
321        except :   
322            crashed()
323        else :   
324            frame.Show(True)
325        return True
326       
327def main():
328    """Program's entry point."""
329    app = PyKotIconApp()
330    app.MainLoop()
331   
332def crashed() :   
333    """Minimal crash method."""
334    import traceback
335    lines = []
336    for line in traceback.format_exception(*sys.exc_info()) :
337        lines.extend([l for l in line.split("\n") if l])
338    msg = "ERROR: ".join(["%s\n" % l for l in (["ERROR: PyKotIcon"] + lines)])
339    sys.stderr.write(msg)
340    sys.stderr.flush()
341   
342def test() :
343    """Runs in test mode (console)."""
344    if len(sys.argv) >= 3 :
345        username = sys.argv[2] 
346    else :   
347        username = win32api.GetUserName()
348    net = CGINetworkInterface(DUMPYKOTA_URL)
349    user = net.getUser(username)
350    print "UserName : ", user.Name
351    print "LimitBy : ", user.LimitBy
352    print "Balance : ", user.Balance
353    for q in user.Quotas :
354        print "\tPrinterName : ", q.PrinterName
355        print "\tPageCounter : ", q.PageCounter
356        print "\tSoftLimit : ", q.SoftLimit
357        print "\tHardLimit : ", q.HardLimit
358        print "\tDateLimit : ", q.DateLimit
359        print
360
361if __name__ == '__main__':
362    if (len(sys.argv) >= 2) and (sys.argv[1] == "--test") :
363        test()
364    else :   
365        main()
366   
Note: See TracBrowser for help on using the browser.