root / pykoticon / trunk / bin / pykoticon @ 75

Revision 75, 17.6 kB (checked in by jerome, 19 years ago)

Added the remaining credits information

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