root / pykoticon / trunk / bin / pykoticon @ 76

Revision 76, 17.7 kB (checked in by jerome, 19 years ago)

More "portable" paths to icons

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