root / pykoticon / trunk / bin / pykoticon @ 65

Revision 65, 14.5 kB (checked in by jerome, 19 years ago)

More cross-platform.
Planned for translations.

  • 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
29import locale
30import gettext
31
32if sys.platform == "win32" :
33    isWindows = 1
34    try :
35        import win32api
36        import win32net
37        import win32netcon
38    except ImportError :   
39        raise ImportError, "Mark Hammond's Win32 Extensions are missing. Please install them."
40else :       
41    isWindows = 0
42    import pwd
43   
44try :   
45    import wxPython.wx
46    import wx
47    import wx.grid as gridlib
48    hasWxPython = 1
49except ImportError :   
50    hasWxPython = 0
51    raise ImportError, "wxPython for Windows is missing. Please install it."
52   
53DUMPYKOTA_URL = "http://cgi.librelogiciel.com/cgi-bin/dumpykota.cgi"
54
55def getCurrentUserName() :
56    """Retrieves the current user's name."""
57    if isWindows :
58        return win32api.GetUserName()
59    else :   
60        try :
61            return pwd.getpwuid(os.geteuid())[0]
62        except :
63            return "** Unknown **"
64       
65class Printer :
66    """A class for PyKota Printers."""
67    def __init__(self, printername, priceperpage, priceperjob):
68        """Initialize printer datas."""
69        self.PrinterName = printername
70        try :
71            self.PricePerPage = float(priceperpage)
72        except (ValueError, TypeError) :
73            self.PricePerPage = 0.0
74        try :
75            self.PricePerJob = float(priceperjob)
76        except (ValueError, TypeError) :
77            self.PricePerJob = 0.0
78
79class UserQuota :
80    """A class for PyKota User Print Quota entries."""
81    def __init__(self, printername, pagecount, softlimit, hardlimit, datelimit):
82        """Initialize user print quota datas."""
83        self.PrinterName = printername
84        try :
85            self.PageCounter = int(pagecount)
86        except (ValueError, TypeError) :
87            self.PageCounter = 0
88        try :
89            self.SoftLimit = int(softlimit)
90        except (ValueError, TypeError) :
91            self.SoftLimit = None
92        try :
93            self.HardLimit = int(hardlimit)
94        except (ValueError, TypeError) :
95            self.HardLimit = None
96        self.DateLimit = datelimit
97       
98class User :
99    """A class for PyKota users."""
100    def __init__(self, username, userinfo, userquotas) :
101        """Initialize user datas."""
102        self.UserName = username
103        self.LimitBy = userinfo["limitby"][0].lower()
104        try :
105            self.Balance = float(userinfo["balance"][0])
106        except (ValueError, TypeError) :
107            self.Balance = 0.0
108        try :
109            self.LifeTimePaid = float(userinfo["lifetimepaid"][0])
110        except (ValueError, TypeError) :
111            self.LifeTimePaid = 0.0
112        self.Quotas = []
113        for i in range(len(userquotas["printername"])) :
114            printername = userquotas["printername"][i]
115            pagecounter = userquotas["pagecounter"][i]
116            softlimit = userquotas["softlimit"][i]
117            hardlimit = userquotas["hardlimit"][i]
118            datelimit = userquotas["datelimit"][i]
119            self.Quotas.append(UserQuota(printername, pagecounter, softlimit, \
120                                         hardlimit, datelimit))
121           
122class CGINetworkInterface :
123    """A class for all network interactions."""
124    def __init__(self, cgiurl, authname=None, authpw=None) :
125        """Initialize CGI connection datas."""
126        self.cgiurl = cgiurl
127        self.authname = authname
128        self.authpw = authpw
129       
130    def retrieveDatas(self, arguments, fieldnames) :
131        """Retrieve datas from the CGI script."""
132        args = { "report" : 1,
133                 "format" : "csv",
134               } 
135        args.update(arguments)           
136        answer = {}
137        try :           
138            url = "%s?%s" % (self.cgiurl, urllib.urlencode(args))
139            u = urllib2.urlopen(url)
140            lines = u.readlines()
141        except IOError, msg :   
142            raise IOError, _("Unable to retrieve %s : %s") % (url, msg)
143        else :   
144            u.close()
145            try :
146                lines = [ line.strip().split(",") for line in lines ]
147                fields = [field[1:-1] for field in lines[0]]
148                indices = [fields.index(fname) for fname in fieldnames]
149                answer = dict([ (fieldname, \
150                                  [ line[fields.index(fieldname)][1:-1] \
151                                    for line in lines[1:] ]) \
152                                for fieldname in fieldnames ])
153            except :   
154                raise ValueError, _("Invalid datas retrieved from %s") % url
155        return answer
156       
157    def getPrinterNames(self) :
158        """Retrieve the printer's names."""
159        arguments = { "report" : 1,
160                      "format" : "csv",
161                      "datatype" : "printers",
162                    } 
163        return self.retrieveDatas(arguments, ["printername"])["printername"]
164       
165    def getUserInfo(self, username) :
166        """Retrieve the user's information."""
167        arguments = { "datatype" : "users",
168                      "filter" : "username=%s" % username,
169                    } 
170        return self.retrieveDatas(arguments, ("limitby", "balance", \
171                                                         "lifetimepaid"))
172       
173    def getUserPQuotas(self, username) :
174        """Retrieve the user's print quota information."""
175        arguments = { "datatype" : "upquotas",
176                      "filter" : "username=%s" % username,
177                    } 
178        return self.retrieveDatas(arguments, ("printername", "pagecounter", \
179                                              "softlimit", "hardlimit", \
180                                              "datelimit"))
181                                             
182    def getUser(self, username) :
183        """Retrieves the user account and quota information."""
184        info = self.getUserInfo(username)
185        if info :
186            quotas = self.getUserPQuotas(username)
187            return User(username, info, quotas)
188   
189class PyKotIconGrid(gridlib.Grid) :   
190    """A class for user print quota entries."""
191    def __init__(self, parent, quotas) :
192        gridlib.Grid.__init__(self, parent, -1)
193        sys.stderr.write("%s\n" % dir(self))
194        self.CreateGrid(len(quotas), 4)
195        self.EnableEditing(False)
196        self.SetColLabelValue(0, _("Page Counter"))
197        self.SetColLabelValue(1, _("Soft Limit"))
198        self.SetColLabelValue(2, _("Hard Limit"))
199        self.SetColLabelValue(3, _("Date Limit"))
200        attr = gridlib.GridCellAttr()
201        attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_RIGHT)
202        attr.SetReadOnly(True)
203        self.SetColAttr(0, attr)
204        self.SetColAttr(1, attr)
205        self.SetColAttr(2, attr)
206        attr = gridlib.GridCellAttr()
207        attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
208        attr.SetReadOnly(True)
209        self.SetColAttr(3, attr)
210        for i in range(len(quotas)) :
211            q = quotas[i]
212            self.SetRowLabelValue(i, quotas[i].PrinterName)
213            self.SetCellValue(i, 0, str(q.PageCounter))
214            self.SetCellValue(i, 1, str(q.SoftLimit))
215            self.SetCellValue(i, 2, str(q.HardLimit))
216            self.SetCellValue(i, 3, str(q.DateLimit))
217            attr = gridlib.GridCellAttr()
218            colour = wx.GREEN
219            if q.SoftLimit is not None :
220                if q.PageCounter >= q.SoftLimit :
221                    colour = wx.RED
222            elif q.HardLimit is not None :       
223                if q.PageCounter >= q.HardLimit :
224                    colour = wx.RED
225            attr.SetBackgroundColour(colour)       
226            self.SetRowAttr(i, attr)
227        self.AutoSize()
228           
229class PyKotIcon(wxPython.wx.wxFrame):
230    """Main class."""
231    def __init__(self, parent, id):
232        wxPython.wx.wxFrame.__init__(self, parent, -1, \
233               _("PyKota Print Quota for user %s") % getCurrentUserName(), \
234               size = (460, -1), \
235               style = wxPython.wx.wxDEFAULT_FRAME_STYLE | wxPython.wx.wxNO_FULL_REPAINT_ON_RESIZE)
236        self.greenicon = wxPython.wx.wxIcon(os.path.join("..", "icons", "pykoticon-green.ico"), \
237                                  wxPython.wx.wxBITMAP_TYPE_ICO)
238        self.redicon = wxPython.wx.wxIcon(os.path.join("..", "icons", "pykoticon-red.ico"), \
239                                  wxPython.wx.wxBITMAP_TYPE_ICO)
240        self.SetIcon(self.greenicon)
241       
242        self.tbicon = wxPython.wx.wxTaskBarIcon()
243        self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
244       
245        wxPython.wx.EVT_TASKBAR_LEFT_DCLICK(self.tbicon, self.OnTaskBarActivate)
246        wxPython.wx.EVT_TASKBAR_RIGHT_UP(self.tbicon, self.OnTaskBarMenu)
247        wxPython.wx.EVT_ICONIZE(self, self.OnIconify)
248       
249        self.TBMENU_RESTORE = wx.NewId()
250        self.TBMENU_CLOSE = wx.NewId()
251        wxPython.wx.EVT_MENU(self.tbicon, self.TBMENU_RESTORE, \
252                                          self.OnTaskBarActivate)
253        wxPython.wx.EVT_MENU(self.tbicon, self.TBMENU_CLOSE, \
254                                          self.OnTaskBarClose)
255        self.menu = wxPython.wx.wxMenu()
256        self.menu.Append(self.TBMENU_RESTORE, _("Show Print Quota"))
257        self.menu.Append(self.TBMENU_CLOSE, _("Quit"))
258       
259        wxPython.wx.EVT_CLOSE(self, self.OnClose)
260       
261        self.TBTIMER = wx.NewId()
262        self.chrono = wxPython.wx.wxTimer(self, self.TBTIMER)
263        wxPython.wx.EVT_TIMER(self, self.TBTIMER, self.OnChronoTimer)
264       
265        self.User = None
266        self.networkInterface = CGINetworkInterface(DUMPYKOTA_URL)
267        self.inTimer = False
268        self.chrono.Start(250) # first time in 0.25 second
269
270    def OnChronoTimer(self, event) :
271        """Retrieves user's data quota information."""
272        # we stop it there, needed because we want to
273        # change the delay the first time.
274        self.chrono.Stop()   
275        if self.inTimer is False : # avoids re-entrance
276            self.inTimer = True
277            self.User = self.networkInterface.getUser(getCurrentUserName())
278            if self.User.LimitBy == "balance" :
279                if self.User.Balance <= 0.0 :
280                    self.tbicon.SetIcon(self.redicon, "PyKotIcon")
281                else :   
282                    self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
283            else :       
284                isRed = False
285                for q in self.User.Quotas :
286                    if q.SoftLimit is not None :
287                        if q.PageCounter >= q.SoftLimit :
288                            isRed = True
289                            break
290                    elif q.HardLimit is not None :       
291                        if q.PageCounter >= q.HardLimit :
292                            isRed = True
293                            break
294                if isRed is True :
295                    self.tbicon.SetIcon(self.redicon, "PyKotIcon")
296                else :   
297                    self.tbicon.SetIcon(self.greenicon, "PyKotIcon")
298                if hasattr(self, "quotasgrid") :   
299                    self.quotasgrid.Close()
300                    self.quotasgrid.Destroy()
301                    del self.quotasgrid
302                self.quotasgrid = PyKotIconGrid(self, self.User.Quotas)   
303            self.inTimer = False
304        # Now we want it every 3 minutes   
305        #self.chrono.Start(1000 * 60 * 3) # every 3 minutes
306        self.chrono.Start(1000 * 20) # every 20 seconds
307   
308    def OnIconify(self, event) :
309        self.Hide()
310
311    def OnTaskBarActivate(self, event) :
312        if self.IsIconized() :
313            self.Iconize(False)
314        if not self.IsShown() :
315            self.Show(True)
316        self.Raise()
317
318    def OnClose(self, event) :
319        if hasattr(self, "chrono") :
320            self.chrono.Stop()
321            del self.chrono
322        if hasattr(self, "menu") :
323            self.menu.Destroy()
324            del self.menu
325        if hasattr(self, "tbicon") :
326            self.tbicon.Destroy()
327            del self.tbicon
328        self.Destroy()
329
330    def OnTaskBarMenu(self, evt) :
331        self.tbicon.PopupMenu(self.menu)
332
333    def OnTaskBarClose(self, evt) :
334        self.Close()
335        wxPython.wx.wxGetApp().ProcessIdle()
336
337class PyKotIconApp(wxPython.wx.wxApp):
338    def OnInit(self) :
339        try :
340            frame = PyKotIcon(None, -1)
341        except :   
342            crashed()
343        else :   
344            frame.Show(True)
345        return True
346       
347def main():
348    """Program's entry point."""
349    try :
350        locale.setlocale(locale.LC_ALL, "")
351    except (locale.Error, IOError) :
352        sys.stderr.write("Problem while setting locale.\n")
353    try :
354        gettext.install("pykoticon")
355    except :
356        gettext.NullTranslations().install()
357    app = PyKotIconApp()
358    app.MainLoop()
359   
360def crashed() :   
361    """Minimal crash method."""
362    import traceback
363    lines = []
364    for line in traceback.format_exception(*sys.exc_info()) :
365        lines.extend([l for l in line.split("\n") if l])
366    msg = "ERROR: ".join(["%s\n" % l for l in (["ERROR: PyKotIcon"] + lines)])
367    sys.stderr.write(msg)
368    sys.stderr.flush()
369   
370def test() :
371    """Runs in test mode (console)."""
372    if len(sys.argv) >= 3 :
373        username = sys.argv[2] 
374    else :   
375        username = getCurrentUserName()
376    net = CGINetworkInterface(DUMPYKOTA_URL)
377    user = net.getUser(username)
378    print "UserName : ", user.UserName
379    print "LimitBy : ", user.LimitBy
380    print "Balance : ", user.Balance
381    for q in user.Quotas :
382        print "\tPrinterName : ", q.PrinterName
383        print "\tPageCounter : ", q.PageCounter
384        print "\tSoftLimit : ", q.SoftLimit
385        print "\tHardLimit : ", q.HardLimit
386        print "\tDateLimit : ", q.DateLimit
387        print
388
389if __name__ == '__main__':
390    if (len(sys.argv) >= 2) and (sys.argv[1] == "--test") :
391        test()
392    else :   
393        main()
394   
Note: See TracBrowser for help on using the browser.