Changeset 3413 for pykota/trunk/bin

Show
Ignore:
Timestamp:
09/27/08 22:02:37 (16 years ago)
Author:
jerome
Message:

Removed unnecessary spaces at EOL.

Location:
pykota/trunk/bin
Files:
21 modified

Legend:

Unmodified
Added
Removed
  • pykota/trunk/bin/autopykota

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    6161                if printer.Exists : 
    6262                    self.logdebug("Printer %s created successfully." % printername) 
    63                 else :     
     63                else : 
    6464                    self.logdebug("Impossible to create printer %s." % printername) 
    6565                printernames = [printername] 
    66             else :     
     66            else : 
    6767                printernames = [printer.Name] + [p.Name for p in self.storage.getParentPrinters(printer)] 
    68              
     68 
    6969            user = self.storage.getUser(username) 
    7070            if not user.Exists : 
     
    7272                if (options.email is None) : 
    7373                    os.system('pkusers --add --limitby balance --balance "%s" --description "%s" "%s"' \ 
    74                                        % (options.initbalance,  
     74                                       % (options.initbalance, 
    7575                                          _("user created with autopykota").encode(self.charset, "replace"), 
    7676                                          username.encode(self.charset))) 
    7777                else : 
    7878                    os.system('pkusers --add --limitby balance --balance "%s" --email "%s" --description "%s" "%s"' \ 
    79                                        % (options.initbalance,  
    80                                           options.email.encode(self.charset),  
     79                                       % (options.initbalance, 
     80                                          options.email.encode(self.charset), 
    8181                                          _("user created with autopykota").encode(self.charset, "replace"), 
    8282                                          username.encode(self.charset))) 
    83                      
     83 
    8484                user = self.storage.getUserFromBackend(username) 
    8585                if user.Exists : 
    8686                    self.logdebug("User %s created successfully." % username) 
    87                 else :     
     87                else : 
    8888                    self.logdebug("Impossible to create user %s." % username) 
    89                  
    90             if user.Exists and printer.Exists :     
     89 
     90            if user.Exists and printer.Exists : 
    9191                userpquota = self.storage.getUserPQuota(user, printer) 
    9292                if not userpquota.Exists : 
     
    9494                                        % (username, printernames)) 
    9595                    os.system('edpykota --add --printer "%s" "%s"' \ 
    96                                 % (','.join(printernames).encode(self.charset),  
     96                                % (','.join(printernames).encode(self.charset), 
    9797                                   username.encode(self.charset))) 
    98                     userpquota = self.storage.getUserPQuotaFromBackend(user,  
     98                    userpquota = self.storage.getUserPQuotaFromBackend(user, 
    9999                                                                       printer) 
    100100                    if userpquota.Exists : 
     
    102102                                            % (username, printername)) 
    103103                        return 0 
    104                     else :     
     104                    else : 
    105105                        self.logdebug("Impossible to create user %s's print quota entry on printer %s." \ 
    106106                                            % (username, printername)) 
     
    110110                                        % (username, printername)) 
    111111                    return 0 
    112             else :         
     112            else : 
    113113                return -1 
    114                  
    115 if __name__ == "__main__" :     
     114 
     115if __name__ == "__main__" : 
    116116    parser = PyKotaOptionParser(description=_("A tool to automate user account creation and initial balance setting. THIS TOOL MUST NOT BE USED FROM THE COMMAND LINE BUT ONLY AS PART OF AN external policy IN pykota.conf, AND MUST NOT BE USED IF YOU WANT TO LIMIT YOUR USERS BY PAGE QUOTA !"), 
    117117                                usage="autopykota { -i | --initbalance value } [options]") 
     
    124124                            dest="email", 
    125125                            help=_("Set the user's email address.")) 
    126                              
     126 
    127127    parser.add_example('--email="@example.com" --initbalance=10.0', 
    128128                       _("This would set the current user's email address to $PYKOTAUSERNAME@example.com, and would set the initial value of his account balance to 10.0 credits.")) 
    129                         
     129 
    130130    run(parser, AutoPyKota) 
  • pykota/trunk/bin/cupspykota

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    5151from pykota.accounter import openAccounter 
    5252from pykota import cups 
    53      
    54 class FakeObject :         
     53 
     54class FakeObject : 
    5555    """Fake object.""" 
    5656    def __init__(self, name) : 
    5757        """Fake init.""" 
    5858        self.Name = name 
    59          
    60 class FakePrinter(FakeObject) :         
     59 
     60class FakePrinter(FakeObject) : 
    6161    """Fake printer instance.""" 
    6262    pass 
    63      
    64 class FakeUser(FakeObject) :     
     63 
     64class FakeUser(FakeObject) : 
    6565    """Fake user instance.""" 
    6666    def __init__(self, name) : 
     
    6868        self.Email = name 
    6969        FakeObject.__init__(self, name) 
    70      
     70 
    7171class CUPSBackend(PyKotaTool) : 
    7272    """Base class for tools with no database access.""" 
     
    8383        self.lockfilename = None 
    8484        self.lockfile = None 
    85          
    86     def deferredInit(self) :     
     85 
     86    def deferredInit(self) : 
    8787        """Deferred initialization.""" 
    8888        PyKotaTool.deferredInit(self) 
     
    9191            username = self.effectiveUserName 
    9292            raise config.PyKotaConfigError, _("User %(username)s is not allowed to read ~pykota/pykotadmin.conf, you must check the permissions.") % locals() 
    93          
    94     def enableSigInt(self) :     
     93 
     94    def enableSigInt(self) : 
    9595        """Enables the SIGINT signal (which raises KeyboardInterrupt).""" 
    9696        signal.signal(signal.SIGINT, self.oldSigIntHandler) 
    97          
    98     def waitForLock(self) :     
     97 
     98    def waitForLock(self) : 
    9999        """Waits until we can acquire the lock file.""" 
    100100        self.logdebug("Waiting for lock %s to become available..." % self.lockfilename) 
     
    104104                # open the lock file, optionally creating it if needed. 
    105105                self.lockfile = open(self.lockfilename, "a+") 
    106                  
     106 
    107107                # we wait indefinitely for the lock to become available. 
    108108                # works over NFS too. 
    109109                fcntl.lockf(self.lockfile, fcntl.LOCK_EX) 
    110110                haslock = True 
    111                  
     111 
    112112                self.logdebug("Lock %s acquired." % self.lockfilename) 
    113                  
     113 
    114114                # Here we save the PID in the lock file, but we don't use 
    115115                # it, because the lock file may be in a directory shared 
     
    120120                self.lockfile.write(str(self.pid)) 
    121121                self.lockfile.flush() 
    122             except IOError, msg :             
     122            except IOError, msg : 
    123123                self.logdebug("I/O Error while waiting for lock %s : %s" % (self.lockfilename, msg)) 
    124124                time.sleep(0.25) 
    125                      
    126     def discoverOtherBackends(self) :     
     125 
     126    def discoverOtherBackends(self) : 
    127127        """Discovers the other CUPS backends. 
    128          
     128 
    129129           Executes each existing backend in turn in device enumeration mode. 
    130130           Returns the list of available backends. 
    131             
     131 
    132132           Unfortunately this method can't output any debug information 
    133133           to stdout or stderr, else CUPS considers that the device is 
     
    151151                    # process doesn't exist anymore 
    152152                    os.remove(lockfilename) 
    153              
     153 
    154154        if not os.path.exists(lockfilename) : 
    155155            lockfile = open(lockfilename, "w") 
     
    159159                                for b in os.listdir(directory) \ 
    160160                                    if os.access(os.path.join(directory, b), os.X_OK) \ 
    161                                         and (b != myname)]  
    162             for backend in allbackends :                             
     161                                        and (b != myname)] 
     162            for backend in allbackends : 
    163163                answer = os.popen(backend, "r") 
    164164                try : 
    165165                    devices = [line.strip() for line in answer.readlines()] 
    166                 except :     
     166                except : 
    167167                    devices = [] 
    168168                status = answer.close() 
    169169                if status is None : 
    170170                    for d in devices : 
    171                         # each line is of the form :  
     171                        # each line is of the form : 
    172172                        # 'xxxx xxxx "xxxx xxx" "xxxx xxx"' 
    173173                        # so we have to decompose it carefully 
     
    186186                        try : 
    187187                            (devicetype, device, name, fullname) = arguments 
    188                         except ValueError :     
     188                        except ValueError : 
    189189                            pass    # ignore this 'bizarre' device 
    190                         else :     
     190                        else : 
    191191                            if name.startswith('"') and name.endswith('"') : 
    192192                                name = name[1:-1] 
     
    202202                             % (self.myname, self.MyName, self.MyName)) 
    203203        return available 
    204                          
    205     def checkCUPSVersion(self) :                     
     204 
     205    def checkCUPSVersion(self) : 
    206206        """Checks if CUPS is not v1.3.4 or higher.""" 
    207207        fullversion = os.environ.get("SOFTWARE", "") 
     
    210210            try : 
    211211               (major, minor, release) = [int(p) for p in vnum.split(".")] 
    212             except ValueError :    
     212            except ValueError : 
    213213                pass 
    214             else :     
     214            else : 
    215215                return (major > 1) \ 
    216216                        or ((major == 1) and (minor > 3)) \ 
    217217                        or ((major == 1) and (minor == 3) and (release >= 4)) 
    218218        return False 
    219          
    220     def initBackendParameters(self) :     
     219 
     220    def initBackendParameters(self) : 
    221221        """Initializes the backend's attributes.""" 
    222         # check that the DEVICE_URI environment variable's value is  
     222        # check that the DEVICE_URI environment variable's value is 
    223223        # prefixed with self.myname otherwise don't touch it. 
    224         # If this is the case, we have to remove the prefix from  
    225         # the environment before launching the real backend  
     224        # If this is the case, we have to remove the prefix from 
     225        # the environment before launching the real backend 
    226226        self.logdebug("Initializing backend...") 
    227          
     227 
    228228        if not self.checkCUPSVersion() : 
    229229            self.printInfo("BEWARE : CUPS is too old. You should use CUPS v1.3.4 or higher.", "error") 
    230          
     230 
    231231        self.PrinterName = os.environ.get("PRINTER", "") 
    232232        directories = [ self.config.getPrinterDirectory(self.PrinterName), 
     
    240240            else : 
    241241                self.printInfo("Insufficient permissions to access to temporary directory %s" % direc, "warn") 
    242                  
     242 
    243243        self.Action = "ALLOW"   # job allowed by default 
    244244        self.Reason = None 
     
    247247            if copies < 1 : 
    248248                raise ValueError 
    249         except (ValueError, TypeError) :     
     249        except (ValueError, TypeError) : 
    250250            self.logdebug("Invalid number of copies '%s', using 1 instead." % sys.argv[4]) 
    251251            copies = 1 
    252252        if len(sys.argv) == 7 : 
    253253            fname = sys.argv[6] # read job's datas from file 
    254         else :     
    255             fname = None        # read job's datas from stdin             
    256              
     254        else : 
     255            fname = None        # read job's datas from stdin 
     256 
    257257        self.Ticket = cups.JobTicket(sys.argv[1].strip(), self.PrinterName, \ 
    258258                                     copies, fname, sys.argv[5].strip()) 
    259         self.UserName = self.Ticket.OriginatingUserName                              
    260                                       
     259        self.UserName = self.Ticket.OriginatingUserName 
     260 
    261261        self.DataFile = (os.path.join(self.Directory, "%s-%s-%s-%s" % \ 
    262262                   (self.myname, self.PrinterName, self.UserName, self.Ticket.JobId))).encode(sys.getfilesystemencoding(), "replace") 
    263          
     263 
    264264        muststartwith = "%s:" % self.myname 
    265265        device_uri = os.environ.get("DEVICE_URI", "") 
     
    268268            device_uri = fulldevice_uri[len(muststartwith):] 
    269269            for i in range(2) : 
    270                 if device_uri.startswith("/") :   
     270                if device_uri.startswith("/") : 
    271271                    device_uri = device_uri[1:] 
    272272        try : 
    273             (backend, destination) = device_uri.split(":", 1)  
    274         except ValueError :     
     273            (backend, destination) = device_uri.split(":", 1) 
     274        except ValueError : 
    275275            if not device_uri : 
    276276                self.logdebug("Not attached to an existing print queue.") 
    277277                backend = "" 
    278278                printerhostname = "" 
    279             else :     
     279            else : 
    280280                raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri 
    281         else :         
     281        else : 
    282282            if backend == "hp" : 
    283283                try : 
    284284                    printerhostname = destination.split("=")[1] # hp:/net/HP_LaserJet_8000_Series?ip=192.168.100.100 
    285                 except IndexError :     
     285                except IndexError : 
    286286                    self.logdebug("Unsupported hplip URI %s" % device_uri) 
    287                     printerhostname = ""  
    288             else :     
     287                    printerhostname = "" 
     288            else : 
    289289                while destination.startswith("/") : 
    290290                    destination = destination[1:] 
    291                 checkauth = destination.split("@", 1)     
     291                checkauth = destination.split("@", 1) 
    292292                if len(checkauth) == 2 : 
    293293                    destination = checkauth[1] 
    294294                printerhostname = destination.split("/")[0].split(":")[0] 
    295              
    296         self.PrinterHostName = printerhostname     
     295 
     296        self.PrinterHostName = printerhostname 
    297297        self.RealBackend = backend 
    298298        self.DeviceURI = device_uri 
    299          
     299 
    300300        if self.Ticket.BillingCode is None : 
    301301            self.OriginalJobBillingCode = None 
    302         else :     
     302        else : 
    303303            self.OriginalJobBillingCode = self.Ticket.BillingCode[:] 
    304              
     304 
    305305        baselockfilename = self.DeviceURI.replace("/", ".") 
    306306        baselockfilename = baselockfilename.replace(":", ".") 
     
    309309        baselockfilename = baselockfilename.replace("@", ".") 
    310310        self.lockfilename = os.path.join(self.Directory, "%s-%s..LCK" % (self.myname, baselockfilename)) 
    311          
     311 
    312312        self.logdebug("Backend : %s" % self.RealBackend) 
    313313        self.logdebug("DeviceURI : %s" % self.DeviceURI) 
     
    319319        self.logdebug("Copies : %s" % self.Ticket.Copies) 
    320320        self.logdebug("Options : %s" % self.Ticket.Options) 
    321         self.logdebug("Directory : %s" % self.Directory)  
     321        self.logdebug("Directory : %s" % self.Directory) 
    322322        self.logdebug("DataFile : %s" % self.DataFile) 
    323323        self.logdebug("JobBillingCode : %s" % self.Ticket.BillingCode) 
    324324        self.logdebug("JobOriginatingHostName : %s" % self.Ticket.OriginatingHostName) 
    325          
     325 
    326326        # fakes some entries to allow for external mailto 
    327327        # before real entries are extracted from the database. 
    328328        self.User = FakeUser(self.UserName) 
    329329        self.Printer = FakePrinter(self.PrinterName) 
    330          
     330 
    331331        self.enableSigInt() 
    332332        self.logdebug("Backend initialized.") 
    333          
     333 
    334334    def overwriteJobAttributes(self) : 
    335335        """Overwrites some of the job's attributes if needed.""" 
     
    337337        # First overwrite the job ticket 
    338338        self.overwriteJobTicket() 
    339          
     339 
    340340        # do we want to strip out the Samba/Winbind domain name ? 
    341341        separator = self.config.getWinbindSeparator() 
    342342        if separator is not None : 
    343343            self.UserName = self.UserName.split(separator)[-1] 
    344              
    345         # this option is deprecated, and we want to tell people     
     344 
     345        # this option is deprecated, and we want to tell people 
    346346        # this is the case. 
    347347        tolower = self.config.getUserNameToLower() 
     
    352352            if self.config.isTrue(tolower) : 
    353353                self.UserName = self.UserName.lower() 
    354                  
    355         # Now use the newer and more complete 'usernamecase' directive.     
    356         casechange = self.config.getUserNameCase()     
     354 
     355        # Now use the newer and more complete 'usernamecase' directive. 
     356        casechange = self.config.getUserNameCase() 
    357357        if casechange != "native" : 
    358358            self.UserName = getattr(self.UserName, casechange)() 
    359              
    360         # do we want to strip some prefix off of titles ?     
     359 
     360        # do we want to strip some prefix off of titles ? 
    361361        stripprefix = self.config.getStripTitle(self.PrinterName) 
    362362        if stripprefix : 
     
    365365                                      % (stripprefix, self.Ticket.Title)) 
    366366                self.Ticket.Title = self.Ticket.Title[len(stripprefix):] 
    367                  
     367 
    368368        self.logdebug("Username : %s" % self.UserName) 
    369369        self.logdebug("BillingCode : %s" % self.Ticket.BillingCode) 
    370370        self.logdebug("Title : %s" % self.Ticket.Title) 
    371371        self.logdebug("Job's attributes sanitizing done.") 
    372                  
     372 
    373373    def didUserConfirm(self) : 
    374374        """Asks for user confirmation through an external script. 
    375          
     375 
    376376           returns False if the end user wants to cancel the job, else True. 
    377377        """ 
    378378        self.logdebug("Checking if we have to ask for user's confirmation...") 
    379         answer = None                          
     379        answer = None 
    380380        confirmationcommand = self.config.getAskConfirmation(self.PrinterName) 
    381381        if confirmationcommand : 
     
    388388                    if answer == "CANCEL" : 
    389389                        break 
    390             except IOError, msg :             
     390            except IOError, msg : 
    391391                self.logdebug("IOError while reading subprocess' output : %s" % msg) 
    392             inputfile.close()     
     392            inputfile.close() 
    393393            self.logdebug("User's confirmation received : %s" % (((answer == "CANCEL") and "CANCEL") or "CONTINUE")) 
    394         else :     
     394        else : 
    395395            self.logdebug("No need to ask for user's confirmation, job processing will continue.") 
    396         return (answer != "CANCEL")     
    397          
    398     def overwriteJobTicket(self) :     
     396        return (answer != "CANCEL") 
     397 
     398    def overwriteJobTicket(self) : 
    399399        """Should we overwrite the job's ticket (username and billingcode) ?""" 
    400400        self.logdebug("Checking if we need to overwrite the job ticket...") 
     
    414414                        self.logdebug("Seen CANCEL command.") 
    415415                        action = "CANCEL" 
    416                     elif line.startswith("USERNAME=") :     
     416                    elif line.startswith("USERNAME=") : 
    417417                        username = line.split("=", 1)[1].strip().decode(self.charset, "replace") 
    418418                        self.logdebug("Seen new username [%s]" % username) 
    419                     elif line.startswith("BILLINGCODE=") :     
     419                    elif line.startswith("BILLINGCODE=") : 
    420420                        billingcode = line.split("=", 1)[1].strip().decode(self.charset, "replace") 
    421421                        self.logdebug("Seen new billing code [%s]" % billingcode) 
     
    423423                        reason = line.split("=", 1)[1].strip().decode(self.charset, "replace") 
    424424                        self.logdebug("Seen new reason [%s]" % reason) 
    425             except IOError, msg :             
     425            except IOError, msg : 
    426426                self.logdebug("IOError while reading subprocess' output : %s" % msg) 
    427             inputfile.close()     
    428              
     427            inputfile.close() 
     428 
    429429            # now overwrite the job's ticket if new data was supplied 
    430430            if action == "DENY" : 
     
    441441                    self.UserName = username 
    442442                if billingcode is not None : 
    443                     self.Ticket.BillingCode = billingcode  
     443                    self.Ticket.BillingCode = billingcode 
    444444        self.logdebug("Job ticket overwriting done.") 
    445              
     445 
    446446    def saveDatasAndCheckSum(self) : 
    447447        """Saves the input datas into a static file.""" 
    448448        self.logdebug("Duplicating data stream into %s" % self.DataFile) 
    449449        mustclose = 0 
    450         outfile = open(self.DataFile, "wb")     
     450        outfile = open(self.DataFile, "wb") 
    451451        if self.Ticket.FileName is not None : 
    452452            infile = open(self.Ticket.FileName, "rb") 
    453453            self.logdebug("Reading input datas from %s" % self.Ticket.FileName) 
    454454            mustclose = 1 
    455         else :     
     455        else : 
    456456            infile = sys.stdin 
    457457            self.logdebug("Reading input datas from stdin") 
     
    461461        checksum = md5.new() 
    462462        while 1 : 
    463             data = infile.read(CHUNK)  
     463            data = infile.read(CHUNK) 
    464464            if not data : 
    465465                break 
    466             sizeread += len(data)     
     466            sizeread += len(data) 
    467467            outfile.write(data) 
    468             checksum.update(data)     
     468            checksum.update(data) 
    469469            if not (dummy % 32) : # Only display every 2 Mb 
    470470                self.logdebug("%s bytes saved..." % sizeread) 
    471             dummy += 1     
    472         if mustclose :     
     471            dummy += 1 
     472        if mustclose : 
    473473            infile.close() 
    474              
     474 
    475475        outfile.close() 
    476         self.JobSizeBytes = sizeread     
     476        self.JobSizeBytes = sizeread 
    477477        self.JobMD5Sum = checksum.hexdigest() 
    478          
     478 
    479479        self.logdebug("JobSizeBytes : %s" % self.JobSizeBytes) 
    480480        self.logdebug("JobMD5Sum : %s" % self.JobMD5Sum) 
    481481        self.logdebug("Data stream duplicated into %s" % self.DataFile) 
    482              
     482 
    483483    def clean(self) : 
    484484        """Cleans up the place.""" 
     
    487487            try : 
    488488                keep = self.config.getPrinterKeepFiles(self.PrinterName) 
    489             except AttributeError :     
     489            except AttributeError : 
    490490                keep = False 
    491491            if not keep : 
     
    497497                else : 
    498498                    self.logdebug("Work file %s has been deleted." % self.DataFile) 
    499             else :     
     499            else : 
    500500                self.logdebug("Work file %s will be kept." % self.DataFile) 
    501         PyKotaTool.clean(self)     
     501        PyKotaTool.clean(self) 
    502502        if self.lockfile is not None : 
    503503            self.logdebug("Unlocking %s..." %  self.lockfilename) 
     
    505505                fcntl.lockf(self.lockfile, fcntl.LOCK_UN) 
    506506                self.lockfile.close() 
    507             except :     
     507            except : 
    508508                self.printInfo("Problem while unlocking %s" % self.lockfilename, "error") 
    509             else :     
     509            else : 
    510510                self.logdebug("%s unlocked." % self.lockfilename) 
    511511        self.logdebug("Clean.") 
    512              
    513     def precomputeJobSize(self) :     
     512 
     513    def precomputeJobSize(self) : 
    514514        """Computes the job size with a software method.""" 
    515515        self.logdebug("Precomputing job's size...") 
     
    518518        self.softwareJobSize = self.preaccounter.getJobSize(None) 
    519519        self.logdebug("Precomputed job's size is %s pages." % self.softwareJobSize) 
    520          
    521     def precomputeJobPrice(self) :     
     520 
     521    def precomputeJobPrice(self) : 
    522522        """Precomputes the job price with a software method.""" 
    523523        self.logdebug("Precomputing job's price...") 
     
    525525        self.logdebug("Precomputed job's price is %.3f credits." \ 
    526526                                   % self.softwareJobPrice) 
    527          
    528     def exportJobInfo(self) :     
     527 
     528    def exportJobInfo(self) : 
    529529        """Exports the actual job's attributes to the environment.""" 
    530530        self.logdebug("Exporting job information to the environment...") 
     
    550550        setenv("PYKOTAPRECOMPUTEDJOBSIZE", str(self.softwareJobSize), self.charset) 
    551551        self.logdebug("Environment updated.") 
    552          
     552 
    553553    def exportUserInfo(self) : 
    554554        """Exports user information to the environment.""" 
     
    559559        setenv("PYKOTALIFETIMEPAID", str(self.User.LifeTimePaid or 0.0), self.charset) 
    560560        setenv("PYKOTAUSERDESCRIPTION", self.User.Description or "", self.charset) 
    561          
     561 
    562562        setenv("PYKOTAPAGECOUNTER", str(self.UserPQuota.PageCounter or 0), self.charset) 
    563563        setenv("PYKOTALIFEPAGECOUNTER", str(self.UserPQuota.LifePageCounter or 0), self.charset) 
     
    566566        setenv("PYKOTADATELIMIT", str(self.UserPQuota.DateLimit), self.charset) 
    567567        setenv("PYKOTAWARNCOUNT", str(self.UserPQuota.WarnCount), self.charset) 
    568          
     568 
    569569        # TODO : move this elsewhere once software accounting is done only once. 
    570570        setenv("PYKOTAPRECOMPUTEDJOBPRICE", str(self.softwareJobPrice), self.charset) 
    571          
     571 
    572572        self.logdebug("Environment updated.") 
    573          
     573 
    574574    def exportPrinterInfo(self) : 
    575575        """Exports printer information to the environment.""" 
     
    584584        setenv("PYKOTAPRICEPERJOB", str(self.Printer.PricePerJob or 0), self.charset) 
    585585        self.logdebug("Environment updated.") 
    586          
     586 
    587587    def exportPhaseInfo(self, phase) : 
    588588        """Exports phase information to the environment.""" 
     
    590590        setenv("PYKOTAPHASE", phase, self.charset) 
    591591        self.logdebug("Environment updated.") 
    592          
     592 
    593593    def exportJobSizeAndPrice(self) : 
    594594        """Exports job's size and price information to the environment.""" 
     
    597597        setenv("PYKOTAJOBPRICE", str(self.JobPrice), self.charset) 
    598598        self.logdebug("Environment updated.") 
    599          
     599 
    600600    def exportReason(self) : 
    601601        """Exports the job's action status and optional reason.""" 
     
    605605            setenv("PYKOTAREASON", self.Reason or "", self.charset) 
    606606        self.logdebug("Environment updated.") 
    607          
    608     def acceptJob(self) :         
     607 
     608    def acceptJob(self) : 
    609609        """Returns the appropriate exit code to tell CUPS all is OK.""" 
    610610        return 0 
    611              
    612     def removeJob(self) :             
     611 
     612    def removeJob(self) : 
    613613        """Returns the appropriate exit code to let CUPS think all is OK. 
    614          
     614 
    615615           Returning 0 (success) prevents CUPS from stopping the print queue. 
    616         """    
     616        """ 
    617617        return 0 
    618          
     618 
    619619    def launchPreHook(self) : 
    620620        """Allows plugging of an external hook before the job gets printed.""" 
     
    624624            retcode = os.system(prehook) 
    625625            self.logdebug("pre-hook exited with status %s." % retcode) 
    626          
     626 
    627627    def launchPostHook(self) : 
    628628        """Allows plugging of an external hook after the job gets printed and/or denied.""" 
     
    632632            retcode = os.system(posthook) 
    633633            self.logdebug("post-hook exited with status %s." % retcode) 
    634              
    635     def improveMessage(self, message) :         
     634 
     635    def improveMessage(self, message) : 
    636636        """Improves a message by adding more informations in it if possible.""" 
    637637        try : 
     
    640640                                        self.Ticket.JobId, \ 
    641641                                        message) 
    642         except :                                                
     642        except : 
    643643            return message 
    644          
    645     def logdebug(self, message) :         
     644 
     645    def logdebug(self, message) : 
    646646        """Improves the debug message before outputting it.""" 
    647647        PyKotaTool.logdebug(self, self.improveMessage(message)) 
    648          
    649     def printInfo(self, message, level="info") :         
     648 
     649    def printInfo(self, message, level="info") : 
    650650        """Improves the informational message before outputting it.""" 
    651651        self.logger.log_message(self.improveMessage(message), level) 
    652      
     652 
    653653    def startingBanner(self, withaccounting) : 
    654654        """Retrieves a starting banner for current printer and returns its content.""" 
     
    656656        self.printBanner(self.config.getStartingBanner(self.PrinterName), withaccounting) 
    657657        self.logdebug("Starting banner retrieved.") 
    658      
     658 
    659659    def endingBanner(self, withaccounting) : 
    660660        """Retrieves an ending banner for current printer and returns its content.""" 
     
    662662        self.printBanner(self.config.getEndingBanner(self.PrinterName), withaccounting) 
    663663        self.logdebug("Ending banner retrieved.") 
    664          
     664 
    665665    def printBanner(self, bannerfileorcommand, withaccounting) : 
    666666        """Reads a banner or generates one through an external command. 
    667          
     667 
    668668           Returns the banner's content in a format which MUST be accepted 
    669669           by the printer. 
     
    691691                try : 
    692692                    fh = open(bannerfileorcommand, 'rb') 
    693                 except IOError, msg :     
     693                except IOError, msg : 
    694694                    self.printInfo("Impossible to open %s : %s" \ 
    695695                                       % (bannerfileorcommand, msg), "error") 
    696                 else :     
     696                else : 
    697697                    self.runOriginalBackend(fh, isBanner=1) 
    698698                    fh.close() 
     
    701701                            self.BannerSize += 1 # TODO : fix this by passing the banner's content through software accounting 
    702702        self.logdebug("Banner printed...") 
    703                  
     703 
    704704    def handleBanner(self, bannertype, withaccounting) : 
    705705        """Handles the banner with or without accounting.""" 
    706706        if withaccounting : 
    707707            acc = "with" 
    708         else :     
     708        else : 
    709709            acc = "without" 
    710710        self.logdebug("Handling %s banner %s accounting..." % (bannertype, acc)) 
     
    735735                        if (avoidduplicatebanners == "YES") : 
    736736                            printbanner = False 
    737                         else :     
    738                             # avoidduplicatebanners is an integer, since NO,  
     737                        else : 
     738                            # avoidduplicatebanners is an integer, since NO, 
    739739                            # YES and 0 are already handled 
    740740                            now = DateTime.now() 
     
    746746                            self.logdebug("Difference with previous job : %.2f seconds. Try to avoid banners for : %.2f seconds." % (difference, avoidduplicatebanners)) 
    747747                            if difference < avoidduplicatebanners : 
    748                                 self.logdebug("Duplicate banner avoided because previous banner is less than %.2f seconds old." % avoidduplicatebanners)  
     748                                self.logdebug("Duplicate banner avoided because previous banner is less than %.2f seconds old." % avoidduplicatebanners) 
    749749                                printbanner = False 
    750750                            else : 
     
    753753                    getattr(self, "%sBanner" % bannertype)(withaccounting) 
    754754        self.logdebug("%s banner done." % bannertype.title()) 
    755          
    756     def sanitizeJobSize(self) :     
     755 
     756    def sanitizeJobSize(self) : 
    757757        """Sanitizes the job's size if needed.""" 
    758758        # TODO : there's a difficult to see bug here when banner accounting is activated and hardware accounting is used. 
     
    772772                    if replacement == "PRECOMPUTED" : 
    773773                        self.JobSize = self.softwareJobSize 
    774                     else :     
     774                    else : 
    775775                        self.JobSize = replacement 
    776776        self.logdebug("Job's size sanitized.") 
    777                          
    778     def getPrinterUserAndUserPQuota(self) :         
     777 
     778    def getPrinterUserAndUserPQuota(self) : 
    779779        """Returns a tuple (policy, printer, user, and user print quota) on this printer. 
    780          
     780 
    781781           "OK" is returned in the policy if both printer, user and user print quota 
    782782           exist in the Quota Storage. 
    783783           Otherwise, the policy as defined for this printer in pykota.conf is returned. 
    784             
     784 
    785785           If policy was set to "EXTERNAL" and one of printer, user, or user print quota 
    786786           doesn't exist in the Quota Storage, then an external command is launched, as 
     
    789789           or users, for example, and finally extracting printer, user and user print 
    790790           quota from the Quota Storage is tried a second time. 
    791             
     791 
    792792           "EXTERNALERROR" is returned in case policy was "EXTERNAL" and an error status 
    793793           was returned by the external command. 
     
    802802                break 
    803803            (policy, args) = self.config.getPrinterPolicy(self.PrinterName) 
    804             if policy == "EXTERNAL" :     
     804            if policy == "EXTERNAL" : 
    805805                commandline = self.formatCommandLine(args, user, printer) 
    806806                if not printer.Exists : 
     
    814814                    policy = "EXTERNALERROR" 
    815815                    break 
    816             else :         
     816            else : 
    817817                if not printer.Exists : 
    818818                    self.printInfo(_("Printer %s not registered in the PyKota system, applying default policy (%s)") % (self.PrinterName, policy)) 
     
    822822                    self.printInfo(_("User %s doesn't have quota on printer %s in the PyKota system, applying default policy (%s)") % (self.UserName, self.PrinterName, policy)) 
    823823                break 
    824                  
    825         if policy == "EXTERNAL" :     
     824 
     825        if policy == "EXTERNAL" : 
    826826            if not printer.Exists : 
    827827                self.printInfo(_("Printer %s still not registered in the PyKota system, job will be rejected") % self.PrinterName) 
     
    830830            if not userpquota.Exists : 
    831831                self.printInfo(_("User %s still doesn't have quota on printer %s in the PyKota system, job will be rejected") % (self.UserName, self.PrinterName)) 
    832         self.Policy = policy          
     832        self.Policy = policy 
    833833        self.Printer = printer 
    834834        self.User = user 
    835835        self.UserPQuota = userpquota 
    836836        self.logdebug("Retrieval of printer, user and user print quota entry done.") 
    837          
    838     def getBillingCode(self) :     
     837 
     838    def getBillingCode(self) : 
    839839        """Extracts the billing code from the database. 
    840           
     840 
    841841           An optional script is launched to notify the user when 
    842842           the billing code is unknown and PyKota was configured to 
     
    858858                    if self.BillingCode.Exists : 
    859859                        self.logdebug(msg + "has been created.") 
    860                     else :     
     860                    else : 
    861861                        self.printInfo(msg + "couldn't be created.", "error") 
    862                 else :     
     862                else : 
    863863                    self.logdebug(msg + "job will be denied.") 
    864864                    self.Action = newaction 
    865                     if script is not None :  
     865                    if script is not None : 
    866866                        self.logdebug(msg + "launching subprocess [%s] to notify user." % script) 
    867867                        os.system(script) 
    868868        self.logdebug("Retrieval of billing code information done.") 
    869          
    870     def checkIfDupe(self) :     
     869 
     870    def checkIfDupe(self) : 
    871871        """Checks if the job is a duplicate, and handles the situation.""" 
    872872        self.logdebug("Checking if the job is a duplicate...") 
     
    889889                    self.logdebug("Duplicate job allowed because previous one is more than %.2f seconds old." % duplicatesdelay) 
    890890                else : 
    891                     # TODO : use the current user's last job instead of   
     891                    # TODO : use the current user's last job instead of 
    892892                    # TODO : the current printer's last job. This would be 
    893893                    # TODO : better but requires an additional database query 
    894                     # TODO : with SQL, and is much more complex with the  
     894                    # TODO : with SQL, and is much more complex with the 
    895895                    # TODO : actual LDAP schema. Maybe this is not very 
    896896                    # TODO : important, because usually duplicate jobs are sucessive. 
     
    900900                        self.Action = "DENY" 
    901901                        self.Reason = _("Duplicate print jobs are not allowed on printer %s.") % self.PrinterName 
    902                     else :     
     902                    else : 
    903903                        self.logdebug("Launching subprocess [%s] to see if duplicate jobs should be allowed or not." % denyduplicates) 
    904904                        fanswer = os.popen(denyduplicates, "r") 
    905905                        self.Action = fanswer.read().strip().upper() 
    906906                        fanswer.close() 
    907                         if self.Action == "DENY" :      
     907                        if self.Action == "DENY" : 
    908908                            self.printInfo("%s : %s." % (msg, _("Subprocess denied printing of a dupe")), "warn") 
    909909                            self.Reason = _("Duplicate print jobs are not allowed on printer %s at this time.") % self.PrinterName 
    910                         else :     
     910                        else : 
    911911                            self.printInfo("%s : %s." % (msg, _("Subprocess allowed printing of a dupe")), "warn") 
    912             else :             
     912            else : 
    913913                self.logdebug("Job doesn't seem to be a duplicate.") 
    914914        self.logdebug("Checking if the job is a duplicate done.") 
    915          
     915 
    916916    def tellUser(self) : 
    917917        """Sends a message to an user.""" 
    918         self.logdebug("Sending some feedback to user %s..." % self.UserName)   
     918        self.logdebug("Sending some feedback to user %s..." % self.UserName) 
    919919        if not self.Reason : 
    920920            self.logdebug("No feedback to send to user %s." % self.UserName) 
    921         else :     
     921        else : 
    922922            (mailto, arguments) = self.config.getMailTo(self.PrinterName) 
    923923            if mailto == "EXTERNAL" : 
    924924                # TODO : clean this again 
    925925                self.externalMailTo(arguments, self.Action, self.User, self.Printer, self.Reason) 
    926             else :     
     926            else : 
    927927                # TODO : clean this again 
    928928                admin = self.config.getAdmin(self.PrinterName) 
     
    934934                if mailto in ("BOTH", "ADMIN") : 
    935935                    destination.append(adminmail) 
    936                 if mailto in ("BOTH", "USER") :     
     936                if mailto in ("BOTH", "USER") : 
    937937                    destination.append(usermail) 
    938                      
     938 
    939939                fullmessage = self.Reason + (_("\n\nYour system administrator :\n\n\t%s - <%s>\n") % (admin, adminmail)) 
    940                 try :     
     940                try : 
    941941                    server = smtplib.SMTP(self.smtpserver) 
    942                 except socket.error, msg :     
     942                except socket.error, msg : 
    943943                    self.printInfo(_("Impossible to connect to SMTP server : %s") % msg, "error") 
    944944                else : 
     
    951951                            if mailto == "BOTH" : 
    952952                                msg["Cc"] = adminmail 
    953                         else :     
     953                        else : 
    954954                            msg["To"] = adminmail 
    955955                        msg["Date"] = email.Utils.formatdate(localtime=True) 
    956956                        server.sendmail(adminmail, destination, msg.as_string()) 
    957                     except smtplib.SMTPException, answer :     
     957                    except smtplib.SMTPException, answer : 
    958958                        try : 
    959959                            for (k, v) in answer.recipients.items() : 
     
    963963                    server.quit() 
    964964            self.logdebug("Feedback sent to user %s." % self.UserName) 
    965                  
    966     def mainWork(self) :     
     965 
     966    def mainWork(self) : 
    967967        """Main work is done here.""" 
    968968        if not self.JobSizeBytes : 
     
    974974            self.tellUser() 
    975975            return self.removeJob() 
    976              
     976 
    977977        self.getPrinterUserAndUserPQuota() 
    978978        if self.Policy == "EXTERNALERROR" : 
     
    989989            self.tellUser() 
    990990            return self.removeJob() 
    991         elif self.Policy == "DENY" :     
     991        elif self.Policy == "DENY" : 
    992992            # Either printer, user or user print quota doesn't exist, 
    993993            # and the job should be rejected. 
     
    10071007            #            be allowed if current user is allowed to print on this printer 
    10081008            return self.doWork() 
    1009         else :     
     1009        else : 
    10101010            self.Reason = _("Invalid policy %s for printer %s") % (self.Policy, self.PrinterName) 
    10111011            self.printInfo(self.Reason, "error") 
    10121012            self.tellUser() 
    10131013            return self.removeJob() 
    1014      
    1015     def doWork(self) :     
     1014 
     1015    def doWork(self) : 
    10161016        """The accounting work is done here.""" 
    10171017        self.precomputeJobPrice() 
     
    10191019        self.exportPrinterInfo() 
    10201020        self.exportPhaseInfo("BEFORE") 
    1021          
    1022         if self.Action not in ("DENY", "CANCEL") :  
     1021 
     1022        if self.Action not in ("DENY", "CANCEL") : 
    10231023            if self.Printer.MaxJobSize and (self.softwareJobSize > self.Printer.MaxJobSize) : 
    10241024                # This printer was set to refuse jobs this large. 
     
    10281028                # because in case of error the user could complain :-) 
    10291029                self.Reason = _("You are not allowed to print so many pages on printer %s at this time.") % self.PrinterName 
    1030              
     1030 
    10311031        if self.Action not in ("DENY", "CANCEL") : 
    10321032            if self.User.LimitBy == "noprint" : 
     
    10341034                self.Action = "DENY" 
    10351035                self.Reason = _("Your account settings forbid you to print at this time.") 
    1036                  
     1036 
    10371037        if self.Action not in ("DENY", "CANCEL") : 
    10381038            # If printing is still allowed at this time, we 
     
    10411041            # save some database queries. 
    10421042            self.getBillingCode() 
    1043              
     1043 
    10441044        if self.Action not in ("DENY", "CANCEL") : 
    10451045            # If printing is still allowed at this time, we 
     
    10481048            # save some database queries. 
    10491049            self.checkIfDupe() 
    1050                      
     1050 
    10511051        if self.Action not in ("DENY", "CANCEL") : 
    10521052            # If printing is still allowed at this time, we 
     
    10561056            if self.User.LimitBy in ('noquota', 'nochange') : 
    10571057                self.logdebug("User %s is allowed to print with no limit, no need to check quota." % self.UserName) 
    1058             elif self.Printer.PassThrough :     
     1058            elif self.Printer.PassThrough : 
    10591059                self.logdebug("Printer %s is in PassThrough mode, no need to check quota." % self.PrinterName) 
    10601060            else : 
     
    10671067                    self.printInfo(_("Print Quota exceeded for user %s on printer %s") % (self.UserName, self.PrinterName)) 
    10681068                    self.Reason = self.config.getHardWarn(self.PrinterName) 
    1069                 elif self.Action == "WARN" :     
     1069                elif self.Action == "WARN" : 
    10701070                    self.printInfo(_("Print Quota low for user %s on printer %s") % (self.UserName, self.PrinterName)) 
    1071                     if self.User.LimitBy and (self.User.LimitBy.lower() == "balance") :  
     1071                    if self.User.LimitBy and (self.User.LimitBy.lower() == "balance") : 
    10721072                        self.Reason = self.config.getPoorWarn() 
    1073                     else :      
     1073                    else : 
    10741074                        self.Reason = self.config.getSoftWarn(self.PrinterName) 
    1075              
    1076         # If job still allowed to print, should we ask for confirmation ?     
    1077         if self.Action not in ("DENY", "CANCEL") :  
     1075 
     1076        # If job still allowed to print, should we ask for confirmation ? 
     1077        if self.Action not in ("DENY", "CANCEL") : 
    10781078            if not self.didUserConfirm() : 
    10791079                self.Action = "CANCEL" 
    10801080                self.Reason = _("Print job cancelled.") 
    10811081                setenv("PYKOTASTATUS", "CANCELLED", self.charset) 
    1082                  
     1082 
    10831083        # exports some new environment variables 
    10841084        self.exportReason() 
    1085          
     1085 
    10861086        # now tell the user if he needs to know something 
    10871087        self.tellUser() 
    1088          
     1088 
    10891089        # launches the pre hook 
    10901090        self.launchPreHook() 
    1091          
     1091 
    10921092        # handle starting banner pages without accounting 
    10931093        self.BannerSize = 0 
     
    10951095        if (self.Action != "CANCEL") and accountbanner in ["ENDING", "NONE"] : 
    10961096            self.handleBanner("starting", 0) 
    1097          
     1097 
    10981098        if self.Action == "DENY" : 
    10991099            self.printInfo(_("Job denied, no accounting will be done.")) 
    1100         elif self.Action == "CANCEL" :     
     1100        elif self.Action == "CANCEL" : 
    11011101            self.printInfo(_("Job cancelled, no accounting will be done.")) 
    11021102        else : 
    11031103            self.printInfo(_("Job accounting begins.")) 
    11041104            self.accounter.beginJob(self.Printer) 
    1105          
     1105 
    11061106        # handle starting banner pages with accounting 
    11071107        if (self.Action != "CANCEL") and accountbanner in ["STARTING", "BOTH"] : 
    11081108            self.handleBanner("starting", 1) 
    1109          
    1110         # pass the job's data to the real backend if needed    
     1109 
     1110        # pass the job's data to the real backend if needed 
    11111111        if self.Action in ("ALLOW", "WARN") : 
    11121112            retcode = self.printJobDatas() 
    1113         else :         
     1113        else : 
    11141114            retcode = self.removeJob() 
    1115          
     1115 
    11161116        # indicate phase change 
    11171117        self.exportPhaseInfo("AFTER") 
    1118          
     1118 
    11191119        # handle ending banner pages with accounting 
    11201120        if (self.Action != "CANCEL") and accountbanner in ["ENDING", "BOTH"] : 
    11211121            self.handleBanner("ending", 1) 
    1122          
     1122 
    11231123        # stops accounting 
    11241124        if self.Action == "DENY" : 
    11251125            self.printInfo(_("Job denied, no accounting has been done.")) 
    1126         elif self.Action == "CANCEL" :     
     1126        elif self.Action == "CANCEL" : 
    11271127            self.printInfo(_("Job cancelled, no accounting has been done.")) 
    11281128        else : 
    11291129            self.accounter.endJob(self.Printer) 
    11301130            self.printInfo(_("Job accounting ends.")) 
    1131          
    1132         # Do all these database changes within a single transaction     
     1131 
     1132        # Do all these database changes within a single transaction 
    11331133        # NB : we don't enclose ALL the changes within a single transaction 
    11341134        # because while waiting for the printer to answer its internal page 
     
    11471147                    self.JobSize = 0 
    11481148                    self.printInfo(_("Job size forced to 0 because the real CUPS backend failed. No accounting will be done."), "warn") 
    1149                 else :     
     1149                else : 
    11501150                    self.printInfo(_("The real CUPS backend failed, but the job will be accounted for anyway."), "warn") 
    1151                      
    1152             # retrieve the job size     
     1151 
     1152            # retrieve the job size 
    11531153            if self.Action == "DENY" : 
    11541154                self.JobSize = 0 
    11551155                self.printInfo(_("Job size forced to 0 because printing is denied.")) 
    1156             elif self.Action == "CANCEL" :      
     1156            elif self.Action == "CANCEL" : 
    11571157                self.JobSize = 0 
    11581158                self.printInfo(_("Job size forced to 0 because printing was cancelled.")) 
    1159             else :     
     1159            else : 
    11601160                self.UserPQuota.resetDenyBannerCounter() 
    1161                 if (self.Action != "PROBLEM") or ("CHARGE" in onbackenderror) :  
     1161                if (self.Action != "PROBLEM") or ("CHARGE" in onbackenderror) : 
    11621162                    self.JobSize = self.accounter.getJobSize(self.Printer) 
    11631163                    self.sanitizeJobSize() 
    11641164                    self.JobSize += self.BannerSize 
    11651165            self.printInfo(_("Job size : %i") % self.JobSize) 
    1166              
     1166 
    11671167            if ((self.Action == "PROBLEM") and ("NOCHARGE" in onbackenderror)) or \ 
    11681168                (self.Action in ("DENY", "CANCEL")) : 
     
    11731173                self.JobPrice = 0.0 
    11741174            else : 
    1175                 # update the quota for the current user on this printer  
     1175                # update the quota for the current user on this printer 
    11761176                self.printInfo(_("Updating user %s's quota on printer %s") % (self.UserName, self.PrinterName)) 
    11771177                self.JobPrice = self.UserPQuota.increasePagesUsage(self.JobSize, self.accounter.inkUsage) 
    1178              
    1179             # adds the current job to history     
     1178 
     1179            # adds the current job to history 
    11801180            self.Printer.addJobToHistory(self.Ticket.JobId, self.User, self.accounter.getLastPageCounter(), \ 
    11811181                                    self.Action, self.JobSize, self.JobPrice, self.Ticket.FileName, \ 
     
    11841184                                    self.softwareJobSize, self.softwareJobPrice) 
    11851185            self.printInfo(_("Job added to history.")) 
    1186              
     1186 
    11871187            if hasattr(self, "BillingCode") and self.BillingCode and self.BillingCode.Exists : 
    11881188                if (self.Action in ("ALLOW", "WARN")) or \ 
     
    11901190                    self.BillingCode.consume(self.JobSize, self.JobPrice) 
    11911191                    self.printInfo(_("Billing code %s was updated.") % self.BillingCode.BillingCode) 
    1192         except :     
     1192        except : 
    11931193            self.storage.rollbackTransaction() 
    11941194            raise 
    1195         else :     
     1195        else : 
    11961196            self.storage.commitTransaction() 
    1197              
     1197 
    11981198        # exports some new environment variables 
    11991199        self.exportJobSizeAndPrice() 
    1200          
     1200 
    12011201        # then re-export user information with new values 
    12021202        self.exportUserInfo() 
    1203          
     1203 
    12041204        # handle ending banner pages without accounting 
    12051205        if (self.Action != "CANCEL") and accountbanner in ["STARTING", "NONE"] : 
    12061206            self.handleBanner("ending", 0) 
    1207                      
     1207 
    12081208        self.launchPostHook() 
    1209              
    1210         return retcode     
    1211                 
    1212     def printJobDatas(self) :            
     1209 
     1210        return retcode 
     1211 
     1212    def printJobDatas(self) : 
    12131213        """Sends the job's datas to the real backend.""" 
    12141214        self.logdebug("Sending job's datas to real backend...") 
    1215          
     1215 
    12161216        delay = 0 
    12171217        number = 1 
     
    12221222                    if (number < 0) or (delay < 0) : 
    12231223                        raise ValueError 
    1224                 except ValueError :     
     1224                except ValueError : 
    12251225                    self.printInfo(_("Incorrect value for the 'onbackenderror' directive in section [%s]") % self.PrinterName, "error") 
    12261226                    delay = 0 
    12271227                    number = 1 
    1228                 else :     
     1228                else : 
    12291229                    break 
    1230         loopcnt = 1  
    1231         while True :             
     1230        loopcnt = 1 
     1231        while True : 
    12321232            if self.Ticket.FileName is None : 
    12331233                infile = open(self.DataFile, "rb") 
    1234             else :     
     1234            else : 
    12351235                infile = None 
    12361236            retcode = self.runOriginalBackend(infile) 
     
    12441244                    time.sleep(delay) 
    12451245                    loopcnt += 1 
    1246                 else :     
     1246                else : 
    12471247                    break 
    1248              
     1248 
    12491249        self.logdebug("Job's datas sent to real backend.") 
    12501250        return retcode 
    1251          
     1251 
    12521252    def runOriginalBackend(self, filehandle=None, isBanner=0) : 
    12531253        """Launches the original backend.""" 
     
    12551255        if not isBanner : 
    12561256            arguments = [os.environ["DEVICE_URI"]] + [a.encode("UTF-8") for a in sys.argv[1:]] 
    1257         else :     
     1257        else : 
    12581258            # For banners, we absolutely WANT 
    12591259            # to remove any filename from the command line ! 
     
    12641264        # TODO : do something about the job title : if we are printing a banner and the backend 
    12651265        # TODO : uses the job's title to name an output file (cups-pdf:// for example), we're stuck ! 
    1266          
     1266 
    12671267        self.logdebug("Starting original backend %s with args %s" % (originalbackend, " ".join(['"%s"' % a.decode("UTF-8") for a in arguments]))) 
    12681268        pid = os.fork() 
     
    12771277            except OSError, msg : 
    12781278                self.logdebug("execve() failed: %s" % msg) 
    1279             self.logdebug("We shouldn't be there !!!")     
     1279            self.logdebug("We shouldn't be there !!!") 
    12801280            os._exit(-1) 
    1281          
    1282         self.logdebug("Waiting for original backend to exit...")     
     1281 
     1282        self.logdebug("Waiting for original backend to exit...") 
    12831283        killed = False 
    12841284        status = -1 
     
    12891289                if err == 4 : 
    12901290                    killed = True 
    1291                      
     1291 
    12921292        if os.WIFEXITED(status) : 
    12931293            status = os.WEXITSTATUS(status) 
     
    12971297                level = "error" 
    12981298                self.Reason = message 
    1299             else :     
     1299            else : 
    13001300                level = "info" 
    13011301            self.printInfo(message, level) 
     
    13091309            self.printInfo(self.Reason, "warn") 
    13101310            return 1 
    1311          
    1312 if __name__ == "__main__" :     
     1311 
     1312if __name__ == "__main__" : 
    13131313    # This is a CUPS backend, we should act and die like a CUPS backend 
    13141314    wrapper = CUPSBackend() 
    13151315    if len(sys.argv) == 1 : 
    13161316        print "\n".join(wrapper.discoverOtherBackends()) 
    1317         sys.exit(0)                 
    1318     elif len(sys.argv) not in (6, 7) :     
     1317        sys.exit(0) 
     1318    elif len(sys.argv) not in (6, 7) : 
    13191319        logerr("ERROR: %s job-id user title copies options [file]\n"\ 
    13201320                              % sys.argv[0]) 
    13211321        sys.exit(1) 
    1322     else :     
     1322    else : 
    13231323        os.environ["PATH"] = "%s:/bin:/usr/bin:/usr/local/bin:/opt/bin:/sbin:/usr/sbin" % os.environ.get("PATH", "") 
    13241324        try : 
     
    13331333                wrapper.accounter = openAccounter(wrapper) 
    13341334                wrapper.precomputeJobSize() 
    1335                 wrapper.exportJobInfo() # exports a first time to give hints to external scripts  
     1335                wrapper.exportJobInfo() # exports a first time to give hints to external scripts 
    13361336                wrapper.overwriteJobAttributes() 
    13371337                wrapper.exportJobInfo() # re-exports in case it was overwritten 
    13381338                retcode = wrapper.mainWork() 
    1339             except KeyboardInterrupt :     
     1339            except KeyboardInterrupt : 
    13401340                wrapper.printInfo(_("Job %s interrupted by the administrator !") % wrapper.Ticket.JobId, "warn") 
    13411341                retcode = 0 
    1342             except SystemExit, err :     
     1342            except SystemExit, err : 
    13431343                retcode = err.code 
    1344             except :     
     1344            except : 
    13451345                try : 
    13461346                    wrapper.crashed("cupspykota backend failed") 
    1347                 except :     
     1347                except : 
    13481348                    crashed("cupspykota backend failed") 
    13491349                retcode = 1 
    1350         finally :         
     1350        finally : 
    13511351            wrapper.clean() 
    13521352        sys.exit(retcode) 
  • pykota/trunk/bin/dumpykota

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    3333from pykota.dumper import DumPyKota 
    3434 
    35 if __name__ == "__main__" :  
     35if __name__ == "__main__" : 
    3636    parser = PyKotaOptionParser(description=_("Data Dumper for PyKota."), 
    3737                                usage="dumpykota [options] [filterexpr]") 
    38                                  
    39     parser.add_option("-d", "--data",                             
     38 
     39    parser.add_option("-d", "--data", 
    4040                            dest="data", 
    4141                            help=_("Select the type of datas to dump. This option is mandatory. Supported data types are : history, payments, billingcodes, users, groups, printers, upquotas, gpquotas, umembers, pmembers, and all. The 'all' value forces the output format to XML.")) 
    42     parser.add_option("-f", "--format",                             
     42    parser.add_option("-f", "--format", 
    4343                            default="csv", 
    4444                            dest="format", 
    4545                            help=_("Select the output format, the default being comma separated values. Supported formats are : csv, ssv, tsv, xml and cups. The 'cups' output format only works when dumping the history, and produces CUPS' page_log compatible output.")) 
    46     parser.add_option("-o", "--output",                             
     46    parser.add_option("-o", "--output", 
    4747                            dest="output", 
    4848                            default=u"-", 
    4949                            help=_("The name of the file the data dump will be written to. The default value is '-', which tells dumpykota to write the dump to stdout.")) 
    50     parser.add_option("-O", "--orderby",                             
     50    parser.add_option("-O", "--orderby", 
    5151                            dest="orderby", 
    5252                            help=_("Change the ordering of the output based on a comma separated list of ordering statements. For example '-username,+printername' would sort the output by descending order of user names and ascending order of printer names. Not all expressions are supported, and you should not use this if you don't know the internal structure of PyKota's database." )) 
    53     parser.add_option("-s", "--sum",                             
     53    parser.add_option("-s", "--sum", 
    5454                            dest="sum", 
    5555                            action="store_true", 
    5656                            default=False, 
    5757                            help=_("Summarize the output. Only available when dumping the printing history or the payments.")) 
    58                                  
     58 
    5959    parser.add_filterexpression("username", _("User's name")) 
    6060    parser.add_filterexpression("groupname", _("Users group's name")) 
     
    6666    parser.add_filterexpression("start", _("Job's date of printing")) 
    6767    parser.add_filterexpression("end", _("Job's date of printing")) 
    68      
    69     parser.add_example('--unit EURO --output /tmp/invoices.pdf start=now-30',  
     68 
     69    parser.add_example('--unit EURO --output /tmp/invoices.pdf start=now-30', 
    7070                       _("This would generate a PDF document containing invoices for all users who have spent some credits last month. Amounts would be in EURO and not VAT information would be included.")) 
    7171 
    7272    parser.add_example("--data history --format csv >myfile.csv", 
    7373                       _("This would dump the whole printing history to stdout in the CSV format, and redirect the output to a file.")) 
    74    
     74 
    7575    parser.add_example("--data users --format xml -o users.xml", 
    7676                       _("This would dump all users into the 'users.xml' file in the XML format.")) 
    77    
     77 
    7878    parser.add_example("--data history printername=HP2100 username=jerome", 
    7979                       _("This would dump jerome's printing history on printer HP2100.")) 
    80    
     80 
    8181    parser.add_example("--data history start=200503 end=20050730234615", 
    8282                       _("This would dump all jobs printer between March 1st 2008 at midnight and July 30th 2008 at 23 hours 46 minutes and 15 seconds, included.")) 
    83     run(parser, DumPyKota)                    
     83    run(parser, DumPyKota) 
  • pykota/trunk/bin/edpykota

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    3838 
    3939  edpykota [options] user1 user2 ... userN 
    40    
     40 
    4141  edpykota [options] group1 group2 ... groupN 
    4242 
     
    4545  -v | --version       Prints edpykota's version number then exits. 
    4646  -h | --help          Prints this message then exits. 
    47    
     47 
    4848  -a | --add           Adds users or groups print quota entries if 
    4949                       they don't exist in database. 
    50                         
     50 
    5151  -d | --delete        Deletes users or groups print quota entries. 
    5252                       Users or groups are never deleted, you have 
     
    5454                       The history will be purge from all matching 
    5555                       jobs, unless -g | --groups is used. 
    56    
     56 
    5757  -P | --printer p     Edit quotas on printer p only. Actually p can 
    5858                       use wildcards characters to select only 
    5959                       some printers. The default value is *, meaning 
    60                        all printers.  
    61                        You can specify several names or wildcards,  
     60                       all printers. 
     61                       You can specify several names or wildcards, 
    6262                       by separating them with commas. 
    63    
    64   -g | --groups        Edit groups print quota entries instead of  
     63 
     64  -g | --groups        Edit groups print quota entries instead of 
    6565                       users print quota entries. 
    66                            
     66 
    6767  -L | --list          Lists users or groups print quota entries. 
    68    
     68 
    6969  -n | --noquota       Sets both soft and hard limits to None for users 
    7070                       or groups print quota entries. 
    71    
     71 
    7272  -r | --reset         Resets the actual page counter for the user 
    73                        or group to zero on the specified printers.  
     73                       or group to zero on the specified printers. 
    7474                       The life time page counter is kept unchanged. 
    75                         
     75 
    7676  -R | --hardreset     Resets the actual and life time page counters 
    77                        for the user or group to zero on the specified  
     77                       for the user or group to zero on the specified 
    7878                       printers. This is a shortcut for '--used 0'. 
    79                         
     79 
    8080  -s | --skipexisting  In combination with the --add option above, tells 
    8181                       edpykota to not modify existing print quota entries. 
    82                         
    83   -S | --softlimit sl  Sets the quota soft limit to sl pages.                        
    84    
     82 
     83  -S | --softlimit sl  Sets the quota soft limit to sl pages. 
     84 
    8585  -H | --hardlimit hl  Sets the quota hard limit to hl pages. 
    86    
     86 
    8787  -I | --increase v    Increase existing Soft and Hard limits by the value 
    8888                       of v. You can prefix v with + or -, if no sign is 
     
    104104  user1 through userN and group1 through groupN can use wildcards 
    105105  if the --add option is not set. 
    106    
    107 examples :                               
     106 
     107examples : 
    108108 
    109109  $ edpykota --add john paul george ringo 
    110    
     110 
    111111  This will create print quota entries for users john, paul, george 
    112112  and ringo on all printers. These print quota entries will have no 
    113113  limit set. 
    114    
     114 
    115115  $ edpykota --printer lp -S 50 -H 60 jerome 
    116    
     116 
    117117  This will set jerome's print quota on the lp printer to a soft limit 
    118118  of 50 pages, and a hard limit of 60 pages. Both user jerome and 
     
    120120  commands, respectively. 
    121121 
    122   $ edpykota -g -S 500 -H 550 financial support             
    123    
     122  $ edpykota -g -S 500 -H 550 financial support 
     123 
    124124  This will set print quota soft limit to 500 pages and hard limit 
    125125  to 550 pages for groups financial and support on all printers. 
    126    
     126 
    127127  $ edpykota --reset jerome "jo*" 
    128    
     128 
    129129  This will reset jerome's page counter to zero on all printers, as 
    130130  well as every user whose name begins with 'jo'. 
     
    132132  You can also reset the life time page counters by using the 
    133133  --hardreset | -R command line option. 
    134    
     134 
    135135  $ edpykota --printer hpcolor --noquota jerome 
    136    
    137   This will tell PyKota to not limit jerome when printing on the  
    138   hpcolor printer. All his jobs will be allowed on this printer, but  
     136 
     137  This will tell PyKota to not limit jerome when printing on the 
     138  hpcolor printer. All his jobs will be allowed on this printer, but 
    139139  accounting of the pages he prints will still be kept. 
    140140  Print Quotas for jerome on other printers are unchanged. 
    141    
     141 
    142142  $ edpykota --delete --printer "HP*,XER*" jerome rachel 
    143    
     143 
    144144  This will delete users jerome and rachel's print quota 
    145145  entries on all printers which name begin with 'HP' or 
    146146  'XER'. The jobs printed by these users on these printers 
    147147  will be deleted from the history. 
    148 """)  
    149          
    150 class EdPyKota(PyKotaTool) :         
     148""") 
     149 
     150class EdPyKota(PyKotaTool) : 
    151151    """A class for edpykota.""" 
    152152    def modifyPQEntry(self, pqkey, pqentry, noquota, softlimit, hardlimit, increase, reset, hardreset, suffix, used) : 
     
    155155            pqentry.setLimits(softlimit, hardlimit) 
    156156        if increase : 
    157             newsoft = (pqentry.SoftLimit or 0) + increase          
    158             newhard = (pqentry.HardLimit or 0) + increase          
     157            newsoft = (pqentry.SoftLimit or 0) + increase 
     158            newhard = (pqentry.HardLimit or 0) + increase 
    159159            if (newsoft >= 0) and (newhard >= 0) : 
    160160                pqentry.setLimits(newsoft, newhard) 
    161             else :     
     161            else : 
    162162                self.printInfo(_("You can't set negative limits for %s") % pqkey, "error") 
    163163        if reset : 
    164164            pqentry.reset() 
    165         if hardreset :     
     165        if hardreset : 
    166166            pqentry.hardreset() 
    167167        if suffix == "User" : 
    168168            if used : 
    169169                pqentry.setUsage(used) 
    170      
     170 
    171171    def main(self, names, options) : 
    172172        """Edit user or group quotas.""" 
    173173        names = self.sanitizeNames(options, names) 
    174         suffix = (options["groups"] and "Group") or "User"         
     174        suffix = (options["groups"] and "Group") or "User" 
    175175        printernames = options["printer"].split(",") 
    176              
     176 
    177177        if not options["list"] : 
    178178            percent = Percent(self) 
     
    182182        if not options["list"] : 
    183183            percent.setSize(len(printers) * len(entries)) 
    184          
     184 
    185185        if options["list"] : 
    186186            for printer in printers : 
     
    198198                            print "    %s" % (_("Warning banners printed : %s") % pqentry.WarnCount) 
    199199                        print 
    200         elif options["delete"] :     
     200        elif options["delete"] : 
    201201            percent.display("\n%s..." % _("Deletion")) 
    202202            getattr(self.storage, "deleteMany%sPQuotas" % suffix)(printers, entries) 
     
    211211                except ValueError : 
    212212                    raise PyKotaCommandLineError, _("Invalid used value %s.") % used 
    213                      
     213 
    214214            increase = options["increase"] 
    215215            if increase : 
     
    218218                except ValueError : 
    219219                    raise PyKotaCommandLineError, _("Invalid increase value %s.") % increase 
    220              
     220 
    221221            noquota = options["noquota"] 
    222             reset = options["reset"]         
     222            reset = options["reset"] 
    223223            hardreset = options["hardreset"] 
    224224            softlimit = hardlimit = None 
     
    229229                        if softlimit < 0 : 
    230230                            raise ValueError 
    231                     except ValueError :     
     231                    except ValueError : 
    232232                        raise PyKotaCommandLineError, _("Invalid softlimit value %s.") % options["softlimit"] 
    233233                if options["hardlimit"] : 
     
    236236                        if hardlimit < 0 : 
    237237                            raise ValueError 
    238                     except ValueError :     
     238                    except ValueError : 
    239239                        raise PyKotaCommandLineError, _("Invalid hardlimit value %s.") % options["hardlimit"] 
    240                 if (softlimit is not None) and (hardlimit is not None) and (hardlimit < softlimit) :         
     240                if (softlimit is not None) and (hardlimit is not None) and (hardlimit < softlimit) : 
    241241                    # error, exchange them 
    242242                    self.printInfo(_("Hard limit %i is less than soft limit %i, values will be exchanged.") % (hardlimit, softlimit)) 
    243243                    (softlimit, hardlimit) = (hardlimit, softlimit) 
    244                 if hardlimit is None :     
     244                if hardlimit is None : 
    245245                    hardlimit = softlimit 
    246246                    if hardlimit is not None : 
    247247                        self.printInfo(_("Undefined hard limit set to soft limit (%s).") % str(hardlimit)) 
    248                 if softlimit is None :     
     248                if softlimit is None : 
    249249                    softlimit = hardlimit 
    250250                    if softlimit is not None : 
    251251                        self.printInfo(_("Undefined soft limit set to hard limit (%s).") % str(softlimit)) 
    252                          
    253             self.storage.beginTransaction()             
     252 
     253            self.storage.beginTransaction() 
    254254            try : 
    255255                if options["add"] : 
    256256                    percent.display("\n%s...\n" % _("Creation")) 
    257                     if not entries :     
     257                    if not entries : 
    258258                        self.printInfo(_("No entry matches %s. Please use pkusers to create them first.") % (" ".join(names)), "warn") 
    259                              
     259 
    260260                    factory = globals()["Storage%sPQuota" % suffix] 
    261261                    for printer in printers : 
     
    270270                                                        hardreset, suffix, used) 
    271271                            oldpqentry = getattr(self.storage, "add%sPQuota" % suffix)(pqentry) 
    272                             if oldpqentry is not None :     
     272                            if oldpqentry is not None : 
    273273                                if skipexisting : 
    274274                                    self.logdebug("%s print quota entry %s@%s already exists, skipping." % (suffix, ename, pname)) 
    275                                 else :     
     275                                else : 
    276276                                    self.logdebug("%s print quota entry %s@%s already exists, will be modified." % (suffix, ename, pname)) 
    277277                                    self.modifyPQEntry(pqkey, oldpqentry, noquota, \ 
     
    279279                                                        increase, reset, \ 
    280280                                                        hardreset, suffix, used) 
    281                                     oldpqentry.save()                     
     281                                    oldpqentry.save() 
    282282                            percent.oneMore() 
    283                 else :         
     283                else : 
    284284                    percent.display("\n%s...\n" % _("Modification")) 
    285285                    for printer in printers : 
     
    287287                            pqkey = "%s@%s" % (entry.Name, printer.Name) 
    288288                            pqentry = getattr(self.storage, "get%sPQuota" % suffix)(entry, printer) 
    289                             if pqentry.Exists :      
     289                            if pqentry.Exists : 
    290290                                self.modifyPQEntry(pqkey, pqentry, noquota, \ 
    291291                                                    softlimit, hardlimit, \ 
    292292                                                    increase, reset, \ 
    293293                                                    hardreset, suffix, used) 
    294                                 pqentry.save()         
     294                                pqentry.save() 
    295295                            percent.oneMore() 
    296             except :                     
     296            except : 
    297297                self.storage.rollbackTransaction() 
    298298                raise 
    299             else :     
     299            else : 
    300300                self.storage.commitTransaction() 
    301                              
     301 
    302302        if not options["list"] : 
    303303            percent.done() 
    304              
    305 if __name__ == "__main__" :  
     304 
     305if __name__ == "__main__" : 
    306306    retcode = 0 
    307307    try : 
     
    316316                        "printer=", "softlimit=", "hardlimit=", \ 
    317317                        "increase=", "used=", "skipexisting"] 
    318          
     318 
    319319        # Initializes the command line tool 
    320320        manager = EdPyKota(doc=__doc__) 
    321321        manager.deferredInit() 
    322          
     322 
    323323        # parse and checks the command line 
    324324        (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options) 
    325          
     325 
    326326        # sets long options 
    327327        options["help"] = options["h"] or options["help"] 
     
    331331        options["printer"] = options["P"] or options["printer"] or defaults["printer"] 
    332332        options["softlimit"] = options["S"] or options["softlimit"] 
    333         options["hardlimit"] = options["H"] or options["hardlimit"]  
    334         options["reset"] = options["r"] or options["reset"]  
     333        options["hardlimit"] = options["H"] or options["hardlimit"] 
     334        options["reset"] = options["r"] or options["reset"] 
    335335        options["noquota"] = options["n"] or options["noquota"] 
    336         options["delete"] = options["d"] or options["delete"]  
    337         options["hardreset"] = options["R"] or options["hardreset"]  
     336        options["delete"] = options["d"] or options["delete"] 
     337        options["hardreset"] = options["R"] or options["hardreset"] 
    338338        options["used"] = options["U"] or options["used"] 
    339339        options["increase"] = options["I"] or options["increase"] 
    340340        options["list"] = options["L"] or options["list"] 
    341341        options["skipexisting"] = options["s"] or options["skipexisting"] 
    342          
     342 
    343343        if options["help"] : 
    344344            manager.display_usage_and_quit() 
     
    354354        else : 
    355355            retcode = manager.main(args, options) 
    356     except KeyboardInterrupt :         
     356    except KeyboardInterrupt : 
    357357        logerr("\nInterrupted with Ctrl+C !\n") 
    358358        retcode = -3 
    359     except PyKotaCommandLineError, msg :      
     359    except PyKotaCommandLineError, msg : 
    360360        logerr("%s : %s\n" % (sys.argv[0], msg)) 
    361361        retcode = -2 
    362     except SystemExit :         
     362    except SystemExit : 
    363363        pass 
    364364    except : 
    365365        try : 
    366366            manager.crashed("edpykota failed") 
    367         except :     
     367        except : 
    368368            crashed("edpykota failed") 
    369369        retcode = -1 
     
    371371    try : 
    372372        manager.storage.close() 
    373     except (TypeError, NameError, AttributeError) :     
     373    except (TypeError, NameError, AttributeError) : 
    374374        pass 
    375          
    376     sys.exit(retcode)     
     375 
     376    sys.exit(retcode) 
  • pykota/trunk/bin/mailandpopup.sh

    r3275 r3413  
    88# the Free Software Foundation, either version 3 of the License, or 
    99# (at your option) any later version. 
    10 #  
     10# 
    1111# This program is distributed in the hope that it will be useful, 
    1212# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1313# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1414# GNU General Public License for more details. 
    15 #  
     15# 
    1616# You should have received a copy of the GNU General Public License 
    1717# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    3737$MESSAGE 
    3838EOF1 
    39 #  
     39# 
    4040# Send some information to root as well 
    4141mail -s "Print Quota problem on printer $PNAME" root <<EOF2 
     
    4343EOF2 
    4444# 
    45 # Launch WinPopup on user's host (may need a real Samba or NT domain)  
     45# Launch WinPopup on user's host (may need a real Samba or NT domain) 
    4646# In some cases the username does not suffice for smbclient to send a message; 
    4747# we must also supply the IP address. This will use smbstatus to get all IPs 
  • pykota/trunk/bin/papwaitprinter.sh

    r3275 r3413  
    88# the Free Software Foundation, either version 3 of the License, or 
    99# (at your option) any later version. 
    10 #  
     10# 
    1111# This program is distributed in the hope that it will be useful, 
    1212# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1313# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1414# GNU General Public License for more details. 
    15 #  
     15# 
    1616# You should have received a copy of the GNU General Public License 
    1717# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    2121PATH=$PATH:/bin:/usr/bin:/usr/local/bin:/opt/bin:/sbin:/usr/sbin 
    2222sleep 5; 
    23 until papstatus -p "$1" | grep -i idle >/dev/null ; do  
    24    sleep 1 ;  
     23until papstatus -p "$1" | grep -i idle >/dev/null ; do 
     24   sleep 1 ; 
    2525done 
  • pykota/trunk/bin/pkbanner

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    2222# 
    2323 
    24 """A banner generator for PyKota"""         
     24"""A banner generator for PyKota""" 
    2525 
    2626import sys 
     
    3434    import reportlab.lib 
    3535    from reportlab.lib.units import cm 
    36 except ImportError :     
     36except ImportError : 
    3737    hasRL = False 
    38 else :     
     38else : 
    3939    hasRL = True 
    40      
     40 
    4141try : 
    42     import PIL.Image  
    43 except ImportError :     
     42    import PIL.Image 
     43except ImportError : 
    4444    hasPIL = False 
    45 else :     
     45else : 
    4646    hasPIL = True 
    47      
     47 
    4848import pykota.appinit 
    4949from pykota.utils import run 
     
    5656from pykota import version 
    5757 
    58 class PyKotaBanner(Tool) :         
     58class PyKotaBanner(Tool) : 
    5959    """A class for pkbanner.""" 
    60     def getVar(self, varname) :             
     60    def getVar(self, varname) : 
    6161        """Extracts a variable from the environment and returns its value or 'Unknown' in the current locale.""" 
    6262        return os.environ.get(varname) or _("Unknown") 
    63          
     63 
    6464    def printVar(self, canvas, x, y, label, value, size, savetoner) : 
    6565        """Outputs a variable onto the PDF canvas. 
    66          
     66 
    6767           Returns the number of points to substract to current Y coordinate. 
    68         """    
     68        """ 
    6969        canvas.saveState() 
    7070        canvas.setFont("Helvetica-Bold", size) 
     
    7979        canvas.restoreState() 
    8080        return (size + 4) 
    81      
     81 
    8282    def genPDF(self, pagesize, logo, url, text, savetoner) : 
    8383        """Generates the banner in PDF format, return the PDF document as a string.""" 
    8484        document = cStringIO.StringIO() 
    8585        c = canvas.Canvas(document, pagesize=pagesize, pageCompression=1) 
    86          
     86 
    8787        c.setAuthor(self.effectiveUserName) 
    8888        c.setTitle(_("PyKota generated Banner")) 
    8989        c.setSubject(_("This is a print banner generated with PyKota")) 
    90          
     90 
    9191        xcenter = pagesize[0] / 2.0 
    9292        ycenter = pagesize[1] / 2.0 
    93                      
    94         ypos = pagesize[1] - (2 * cm)             
    95          
     93 
     94        ypos = pagesize[1] - (2 * cm) 
     95 
    9696        if logo : 
    97             try :     
     97            try : 
    9898                imglogo = PIL.Image.open(logo) 
    99             except :     
     99            except : 
    100100                self.printInfo("Unable to open image %s" % logo, "warn") 
    101101            else : 
    102102                (width, height) = imglogo.size 
    103                 multi = float(width) / (8 * cm)  
     103                multi = float(width) / (8 * cm) 
    104104                width = float(width) / multi 
    105105                height = float(height) / multi 
     
    107107                ypos -= height 
    108108                c.drawImage(logo, xpos, ypos, width, height) 
    109          
     109 
    110110        # New top 
    111111        xpos = pagesize[0] / 5.0 
    112112        ypos -= (1 * cm) + 20 
    113          
     113 
    114114        printername = self.getVar("PYKOTAPRINTERNAME") 
    115115        username = self.getVar("PYKOTAUSERNAME") 
    116116        accountbanner = self.config.getAccountBanner(printername) 
    117          
     117 
    118118        # Outputs the username 
    119         ypos -= self.printVar(c, xcenter, ypos, _("Username"), username, 20, savetoner)  
    120          
    121         # Text     
     119        ypos -= self.printVar(c, xcenter, ypos, _("Username"), username, 20, savetoner) 
     120 
     121        # Text 
    122122        if text : 
    123             ypos -= self.printVar(c, xcenter, ypos, _("More Info"), text, 20, savetoner)  
    124          
     123            ypos -= self.printVar(c, xcenter, ypos, _("More Info"), text, 20, savetoner) 
     124 
    125125        # Printer and Job Id 
    126126        job = "%s - %s" % (printername, self.getVar("PYKOTAJOBID")) 
    127         ypos -= self.printVar(c, xcenter, ypos, _("Job"), job, 14, savetoner)  
    128          
     127        ypos -= self.printVar(c, xcenter, ypos, _("Job"), job, 14, savetoner) 
     128 
    129129        # Current date (TODO : at the time the banner was printed ! Change this to job's submission date) 
    130130        datetime = time.strftime("%c", time.localtime()).decode(self.charset, "replace") 
    131         ypos -= self.printVar(c, xcenter, ypos, _("Date"), datetime, 14, savetoner)  
    132          
     131        ypos -= self.printVar(c, xcenter, ypos, _("Date"), datetime, 14, savetoner) 
     132 
    133133        # Result of the print job 
    134134        action = self.getVar("PYKOTAACTION") 
    135135        if action == "ALLOW" : 
    136136            action = _("Allowed") 
    137         elif action == "DENY" :     
     137        elif action == "DENY" : 
    138138            action = _("Denied") 
    139         elif action == "WARN" :     
     139        elif action == "WARN" : 
    140140            action = _("Allowed with Warning") 
    141         elif action == "PROBLEM" :     
     141        elif action == "PROBLEM" : 
    142142            # should never occur 
    143143            action = _("Problem") 
    144         elif action == "CANCEL" :     
     144        elif action == "CANCEL" : 
    145145            # should never occur 
    146146            action = _("Cancelled") 
    147         ypos -= self.printVar(c, xcenter, ypos, _("Result"), action, 14, savetoner)  
    148          
     147        ypos -= self.printVar(c, xcenter, ypos, _("Result"), action, 14, savetoner) 
     148 
    149149        # skip some space 
    150150        ypos -= 20 
    151          
     151 
    152152        # Outputs title and filename 
    153153        # We put them at x=0.25*pagewidth so that the line is long enough to hold them 
    154154        title = self.getVar("PYKOTATITLE") 
    155         ypos -= self.printVar(c, xcenter / 2.0, ypos, _("Title"), title, 10, savetoner)  
    156          
     155        ypos -= self.printVar(c, xcenter / 2.0, ypos, _("Title"), title, 10, savetoner) 
     156 
    157157        filename = self.getVar("PYKOTAFILENAME") 
    158         ypos -= self.printVar(c, xcenter / 2.0, ypos, _("Filename"), filename, 10, savetoner)  
    159          
     158        ypos -= self.printVar(c, xcenter / 2.0, ypos, _("Filename"), filename, 10, savetoner) 
     159 
    160160        # skip some space 
    161161        ypos -= 20 
    162          
     162 
    163163        # Now outputs the user's account balance or page counter 
    164         ypos -= self.printVar(c, xcenter, ypos, _("Pages printed so far on %s") % printername, self.getVar("PYKOTAPAGECOUNTER"), 14, savetoner)  
     164        ypos -= self.printVar(c, xcenter, ypos, _("Pages printed so far on %s") % printername, self.getVar("PYKOTAPAGECOUNTER"), 14, savetoner) 
    165165        limitby = self.getVar("PYKOTALIMITBY") 
    166         if limitby == "balance" :   
    167             ypos -= self.printVar(c, xcenter, ypos, _("Account balance"), self.getVar("PYKOTABALANCE"), 14, savetoner)  
     166        if limitby == "balance" : 
     167            ypos -= self.printVar(c, xcenter, ypos, _("Account balance"), self.getVar("PYKOTABALANCE"), 14, savetoner) 
    168168        elif limitby == "quota" : 
    169             ypos -= self.printVar(c, xcenter, ypos, _("Soft Limit"), self.getVar("PYKOTASOFTLIMIT"), 14, savetoner)  
    170             ypos -= self.printVar(c, xcenter, ypos, _("Hard Limit"), self.getVar("PYKOTAHARDLIMIT"), 14, savetoner)  
    171             ypos -= self.printVar(c, xcenter, ypos, _("Date Limit"), self.getVar("PYKOTADATELIMIT"), 14, savetoner)  
     169            ypos -= self.printVar(c, xcenter, ypos, _("Soft Limit"), self.getVar("PYKOTASOFTLIMIT"), 14, savetoner) 
     170            ypos -= self.printVar(c, xcenter, ypos, _("Hard Limit"), self.getVar("PYKOTAHARDLIMIT"), 14, savetoner) 
     171            ypos -= self.printVar(c, xcenter, ypos, _("Date Limit"), self.getVar("PYKOTADATELIMIT"), 14, savetoner) 
    172172        else : 
    173173            if limitby == "noquota" : 
    174174                msg = _("No Limit") 
    175             elif limitby == "nochange" :     
     175            elif limitby == "nochange" : 
    176176                msg = _("No Accounting") 
    177             elif limitby == "noprint" :     
     177            elif limitby == "noprint" : 
    178178                msg = _("Forbidden") 
    179             else :     
     179            else : 
    180180                msg = _("Unknown") 
    181181            ypos -= self.printVar(c, xcenter, ypos, _("Printing Mode"), msg, 14, savetoner) 
    182              
     182 
    183183        # URL 
    184184        if url : 
     
    189189            c.drawCentredString(xcenter, 2 * cm, url) 
    190190            c.restoreState() 
    191          
     191 
    192192        c.showPage() 
    193193        c.save() 
    194194        return document.getvalue() 
    195          
     195 
    196196    def main(self, arguments, options) : 
    197197        """Generates a banner.""" 
     
    200200        if not hasPIL : 
    201201            raise PyKotaToolError, "The Python Imaging Library is missing. Download it from http://www.pythonware.com/downloads" 
    202              
    203         self.logdebug("Generating the banner in PDF format...")     
     202 
     203        self.logdebug("Generating the banner in PDF format...") 
    204204        doc = self.genPDF(getPageSize(options.pagesize), 
    205                           options.logo.strip().encode(sys.getfilesystemencoding(), "replace"),  
    206                           options.url.strip(),  
    207                           " ".join(arguments).strip(),  
     205                          options.logo.strip().encode(sys.getfilesystemencoding(), "replace"), 
     206                          options.url.strip(), 
     207                          " ".join(arguments).strip(), 
    208208                          options.savetoner / 100.0) 
    209          
    210         self.logdebug("Converting the banner to PostScript...")     
     209 
     210        self.logdebug("Converting the banner to PostScript...") 
    211211        command = "gs -q -dNOPAUSE -dBATCH -dPARANOIDSAFER -sDEVICE=pswrite -sOutputFile=- -" 
    212212        subpr = subprocess.Popen(command, 
     
    215215                                 stdout=subprocess.PIPE, 
    216216                                 stderr=subprocess.PIPE) 
    217         try :                          
     217        try : 
    218218            (out, err) = subpr.communicate(doc) 
    219         except OSError, msg :     
     219        except OSError, msg : 
    220220            raise PyKotaToolError, _("Impossible to execute '%(command)s'") % locals() 
    221221        status = subpr.wait() 
     
    255255    parser.add_example('--logo="" --savetoner=75', 
    256256                       _("This would generate a banner in the default page size, with no logo, and text luminosity would be increased by 75%.")) 
    257                         
     257 
    258258    run(parser, PyKotaBanner) 
  • pykota/trunk/bin/pkbcodes

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    3535from pykota.storage import StorageBillingCode 
    3636 
    37 class PKBcodes(PyKotaTool) :         
     37class PKBcodes(PyKotaTool) : 
    3838    """A class for a billing codes manager.""" 
    3939    def modifyBillingCode(self, billingcode, reset, description) : 
    4040        """Modifies a billing code.""" 
    4141        if reset : 
    42             billingcode.reset()     
     42            billingcode.reset() 
    4343        if description is not None : # NB : "" is allowed ! 
    4444            billingcode.setDescription(description) 
    45          
     45 
    4646    def main(self, names, options) : 
    4747        """Manage billing codes.""" 
    4848        if options.action != "list" : 
    4949            self.adminOnly() 
    50             
     50 
    5151        islist = (options.action == "list") 
    5252        isadd = (options.action == "add") 
    5353        isdelete = (options.action == "delete") 
    54          
     54 
    5555        if not names : 
    5656            if isdelete or isadd : 
    5757                raise PyKotaCommandLineError, _("You must specify billing codes on the command line.") 
    58             names = [u"*"]     
    59              
     58            names = [u"*"] 
     59 
    6060        if not islist : 
    6161            percent = Percent(self) 
    62              
     62 
    6363        if not isadd : 
    6464            if not islist : 
     
    7373            if not islist : 
    7474                percent.setSize(len(billingcodes)) 
    75                          
     75 
    7676        if islist : 
    7777            for billingcode in billingcodes : 
     
    8383                       billingcode.Balance, \ 
    8484                       _("credits"))) 
    85         elif isdelete :     
     85        elif isdelete : 
    8686            percent.display("\n%s..." % _("Deletion")) 
    8787            self.storage.deleteManyBillingCodes(billingcodes) 
     
    9191            if description : 
    9292                description = description.strip() 
    93              
     93 
    9494            self.storage.beginTransaction() 
    9595            try : 
    96                 if isadd :     
     96                if isadd : 
    9797                    percent.display("%s...\n" % _("Creation")) 
    9898                    percent.setSize(len(names)) 
    9999                    for bname in names : 
    100100                        billingcode = StorageBillingCode(self.storage, bname) 
    101                         self.modifyBillingCode(billingcode,  
    102                                                options.reset,  
     101                        self.modifyBillingCode(billingcode, 
     102                                               options.reset, 
    103103                                               description) 
    104104                        oldbillingcode = self.storage.addBillingCode(billingcode) 
     
    106106                            if options.skipexisting : 
    107107                                self.logdebug(_("Billing code '%(bname)s' already exists, skipping.") % locals()) 
    108                             else :     
     108                            else : 
    109109                                self.logdebug(_("Billing code '%(bname)s' already exists, will be modified.") % locals()) 
    110                                 self.modifyBillingCode(oldbillingcode,  
    111                                                        options.reset,  
     110                                self.modifyBillingCode(oldbillingcode, 
     111                                                       options.reset, 
    112112                                                       description) 
    113113                                oldbillingcode.save() 
    114114                        percent.oneMore() 
    115                 else :         
     115                else : 
    116116                    percent.display("\n%s...\n" % _("Modification")) 
    117117                    for billingcode in billingcodes : 
    118                         self.modifyBillingCode(billingcode,  
    119                                                options.reset,  
     118                        self.modifyBillingCode(billingcode, 
     119                                               options.reset, 
    120120                                               description) 
    121                         billingcode.save()     
     121                        billingcode.save() 
    122122                        percent.oneMore() 
    123             except :                     
     123            except : 
    124124                self.storage.rollbackTransaction() 
    125125                raise 
    126             else :     
     126            else : 
    127127                self.storage.commitTransaction() 
    128                          
     128 
    129129        if not islist : 
    130130            percent.done() 
    131                       
    132 if __name__ == "__main__" :  
     131 
     132if __name__ == "__main__" : 
    133133    parser = PyKotaOptionParser(description=_("A billing codes manager for PyKota."), 
    134134                                usage="pkbcodes [options] code1 code2 ... codeN") 
     
    159159                            dest="skipexisting", 
    160160                            help=_("If --add is used, ensure that existing billing codes won't be modified.")) 
    161                              
     161 
    162162    parser.add_example('-D "Financial Department" financial', 
    163163                       _("Would create a billing code labelled 'financial' with the specified textual description.")) 
    164     parser.add_example('--delete "fin*"',  
     164    parser.add_example('--delete "fin*"', 
    165165                       _("Would delete all billing codes which label begins with 'fin'. Matching jobs in the printing history wouldn't be deleted though.")) 
    166166    parser.add_example("--list", 
    167167                       _("Would display details about all existing billing codes.")) 
    168                         
     168 
    169169    (options, arguments) = parser.parse_args() 
    170     run(parser, PKBcodes)                    
     170    run(parser, PKBcodes) 
  • pykota/trunk/bin/pkinvoice

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    3434    from reportlab.lib import pagesizes 
    3535    from reportlab.lib.units import cm 
    36 except ImportError :     
     36except ImportError : 
    3737    hasRL = False 
    38 else :     
     38else : 
    3939    hasRL = True 
    40      
     40 
    4141try : 
    42     import PIL.Image  
    43 except ImportError :     
     42    import PIL.Image 
     43except ImportError : 
    4444    hasPIL = False 
    45 else :     
     45else : 
    4646    hasPIL = True 
    4747 
     
    5656from pykota.tool import Percent, PyKotaTool 
    5757 
    58 class PKInvoice(PyKotaTool) :         
     58class PKInvoice(PyKotaTool) : 
    5959    """A class for invoice generator.""" 
    6060    validfilterkeys = [ "username", 
     
    6666                        "end", 
    6767                      ] 
    68                        
     68 
    6969    def printVar(self, label, value, size) : 
    7070        """Outputs a variable onto the PDF canvas. 
    71          
     71 
    7272           Returns the number of points to substract to current Y coordinate. 
    73         """    
     73        """ 
    7474        xcenter = (self.pagesize[0] / 2.0) - 1*cm 
    7575        self.canvas.saveState() 
     
    8282        self.canvas.restoreState() 
    8383        self.ypos -= (size + 4) 
    84          
     84 
    8585    def pagePDF(self, invoicenumber, name, values, unit, vat) : 
    8686        """Generates a new page in the PDF document.""" 
     
    9696            datetime = time.strftime("%c", time.localtime()).decode(self.charset, "replace") 
    9797            self.printVar(_("Edited on"), datetime, 14) 
    98                  
     98 
    9999            self.ypos -= 20 
    100100            self.printVar(_("Number of jobs printed"), str(values["nbjobs"]), 18) 
     
    107107            self.canvas.showPage() 
    108108            return 1 
    109         return 0     
    110          
     109        return 0 
     110 
    111111    def initPDF(self, logo) : 
    112112        """Initializes the PDF document.""" 
    113         self.pdfDocument = cStringIO.StringIO()         
     113        self.pdfDocument = cStringIO.StringIO() 
    114114        self.canvas = c = canvas.Canvas(self.pdfDocument, \ 
    115115                                        pagesize=self.pagesize, \ 
    116116                                        pageCompression=1) 
    117          
     117 
    118118        c.setAuthor(self.effectiveUserName) 
    119119        c.setTitle(_("PyKota invoices")) 
    120120        c.setSubject(_("Invoices generated with PyKota")) 
    121          
     121 
    122122        self.canvas.beginForm("background") 
    123123        self.canvas.saveState() 
    124          
    125         self.ypos = self.pagesize[1] - (2 * cm)             
    126          
     124 
     125        self.ypos = self.pagesize[1] - (2 * cm) 
     126 
    127127        xcenter = self.pagesize[0] / 2.0 
    128128        if logo : 
    129             try :     
     129            try : 
    130130                imglogo = PIL.Image.open(logo) 
    131             except IOError :     
     131            except IOError : 
    132132                self.printInfo("Unable to open image %s" % logo, "warn") 
    133133            else : 
    134134                (width, height) = imglogo.size 
    135                 multi = float(width) / (8 * cm)  
     135                multi = float(width) / (8 * cm) 
    136136                width = float(width) / multi 
    137137                height = float(height) / multi 
     
    140140                                  self.ypos, \ 
    141141                                  width, height) 
    142          
     142 
    143143        self.ypos -= (cm + 20) 
    144144        self.canvas.setFont("Helvetica-Bold", 14) 
     
    146146        msg = _("Here's the invoice for your printouts") 
    147147        self.canvas.drawCentredString(xcenter, self.ypos, "%s :" % msg) 
    148          
     148 
    149149        self.yorigine = self.ypos 
    150150        self.canvas.restoreState() 
    151151        self.canvas.endForm() 
    152          
    153     def endPDF(self, fname) :     
     152 
     153    def endPDF(self, fname) : 
    154154        """Flushes the PDF generator.""" 
    155155        self.canvas.save() 
    156         if fname != "-" :         
     156        if fname != "-" : 
    157157            outfile = open(fname, "w") 
    158158            outfile.write(self.pdfDocument.getvalue()) 
    159159            outfile.close() 
    160         else :     
     160        else : 
    161161            sys.stdout.write(self.pdfDocument.getvalue()) 
    162162            sys.stdout.flush() 
    163          
     163 
    164164    def genInvoices(self, peruser, logo, outfname, firstnumber, unit, vat) : 
    165165        """Generates the invoices file.""" 
     
    168168            if outfname != "-" : 
    169169                percent.display("%s...\n" % _("Generating invoices")) 
    170                  
     170 
    171171            self.initPDF(logo) 
    172172            number = firstnumber 
     
    175175                if outfname != "-" : 
    176176                    percent.oneMore() 
    177                      
     177 
    178178            if number > firstnumber : 
    179179                self.endPDF(outfname) 
    180                  
     180 
    181181            if outfname != "-" : 
    182182                percent.done() 
    183          
     183 
    184184    def main(self, arguments, options) : 
    185185        """Generate invoices.""" 
     
    188188        if not hasPIL : 
    189189            raise PyKotaToolError, "The Python Imaging Library is missing. Download it from http://www.pythonware.com/downloads" 
    190              
     190 
    191191        self.adminOnly() 
    192          
     192 
    193193        self.pagesize = getPageSize(options.pagesize) 
    194              
     194 
    195195        extractonly = {} 
    196196        for filterexp in arguments : 
     
    200200                    filterkey = filterkey.lower() 
    201201                    if filterkey not in self.validfilterkeys : 
    202                         raise ValueError                 
    203                 except ValueError :     
     202                        raise ValueError 
     203                except ValueError : 
    204204                    raise PyKotaCommandLineError, _("Invalid filter value [%s], see help.") % filterexp 
    205                 else :     
     205                else : 
    206206                    extractonly.update({ filterkey : filtervalue }) 
    207              
     207 
    208208        percent = Percent(self) 
    209209        outfname = options.output.strip().encode(sys.getfilesystemencoding()) 
    210210        if outfname != "-" : 
    211211            percent.display("%s..." % _("Extracting datas")) 
    212              
    213         username = extractonly.get("username")     
     212 
     213        username = extractonly.get("username") 
    214214        if username : 
    215215            user = self.storage.getUser(username) 
    216216        else : 
    217217            user = None 
    218              
    219         printername = extractonly.get("printername")     
     218 
     219        printername = extractonly.get("printername") 
    220220        if printername : 
    221221            printer = self.storage.getPrinter(printername) 
    222         else :     
     222        else : 
    223223            printer = None 
    224              
     224 
    225225        start = extractonly.get("start") 
    226226        end = extractonly.get("end") 
    227227        (start, end) = self.storage.cleanDates(start, end) 
    228          
    229         jobs = self.storage.retrieveHistory(user=user,     
    230                                             printer=printer,  
     228 
     229        jobs = self.storage.retrieveHistory(user=user, 
     230                                            printer=printer, 
    231231                                            hostname=extractonly.get("hostname"), 
    232232                                            billingcode=extractonly.get("billingcode"), 
     
    235235                                            end=end, 
    236236                                            limit=0) 
    237              
    238         peruser = {}                                     
    239         nbjobs = 0                                     
    240         nbpages = 0                                             
     237 
     238        peruser = {} 
     239        nbjobs = 0 
     240        nbpages = 0 
    241241        nbcredits = 0.0 
    242242        percent.setSize(len(jobs)) 
    243243        if outfname != "-" : 
    244244            percent.display("\n") 
    245         for job in jobs :                                     
     245        for job in jobs : 
    246246            if job.JobSize and (job.JobAction not in ("DENY", "CANCEL", "REFUND")) : 
    247247                nbpages += job.JobSize 
     
    256256        if outfname != "-" : 
    257257            percent.done() 
    258         self.genInvoices(peruser,  
    259                          options.logo.strip().encode(sys.getfilesystemencoding()),  
    260                          outfname,  
    261                          options.number,  
    262                          options.unit or _("Credits"),  
     258        self.genInvoices(peruser, 
     259                         options.logo.strip().encode(sys.getfilesystemencoding()), 
     260                         outfname, 
     261                         options.number, 
     262                         options.unit or _("Credits"), 
    263263                         options.vat) 
    264         if outfname != "-" :     
     264        if outfname != "-" : 
    265265            nbusers = len(peruser) 
    266266            print _("Invoiced %(nbusers)i users for %(nbjobs)i jobs, %(nbpages)i pages and %(nbcredits).3f credits") \ 
    267267                     % locals() 
    268                       
    269 if __name__ == "__main__" :  
     268 
     269if __name__ == "__main__" : 
    270270    parser = PyKotaOptionParser(description=_("Invoice generator for PyKota."), 
    271271                                usage="pkinvoice [options] [filterexpr]") 
     
    281281                            default=u"A4", 
    282282                            help=_("Set the size of the page. Most well known page sizes are recognized, like 'A4' or 'Letter' to name a few. The default page size is %default.")) 
    283     parser.add_option("-n", "--number",                         
     283    parser.add_option("-n", "--number", 
    284284                            dest="number", 
    285285                            type="int", 
     
    293293                            default=u"-", 
    294294                            help=_("The name of the file to which the PDF invoices will be written. If not set or set to '%default', the PDF document will be sent to the standard output.")) 
    295                              
     295 
    296296    # TODO : due to Python's optparse.py bug #1498146 fixed in rev 46861 
    297297    # TODO : we can't use 'default=_("Credits")' for this option 
    298     parser.add_option("-u", "--unit",                    
     298    parser.add_option("-u", "--unit", 
    299299                            dest="unit", 
    300300                            type="string", 
    301301                            help=_("The name of the unit to use on the invoices. The default value is 'Credits' or its locale translation.")) 
    302                              
    303     parser.add_option("-V", "--vat",  
     302 
     303    parser.add_option("-V", "--vat", 
    304304                            dest="vat", 
    305305                            type="float", 
     
    308308                            default=0.0, 
    309309                            help=_("The value in percent of the applicable VAT to be exposed. The default is %default, meaning no VAT.")) 
    310                              
    311     parser.add_filterexpression("username", _("User's name"))                         
    312     parser.add_filterexpression("printername", _("Printer's name"))                         
    313     parser.add_filterexpression("hostname", _("Host's name"))                         
    314     parser.add_filterexpression("jobid", _("Job's id"))                         
     310 
     311    parser.add_filterexpression("username", _("User's name")) 
     312    parser.add_filterexpression("printername", _("Printer's name")) 
     313    parser.add_filterexpression("hostname", _("Host's name")) 
     314    parser.add_filterexpression("jobid", _("Job's id")) 
    315315    parser.add_filterexpression("billingcode", _("Job's billing code")) 
    316     parser.add_filterexpression("start", _("Job's date of printing"))                         
    317     parser.add_filterexpression("end", _("Job's date of printing"))                         
    318      
    319     parser.add_example('--unit EURO --output /tmp/invoices.pdf start=now-30',  
     316    parser.add_filterexpression("start", _("Job's date of printing")) 
     317    parser.add_filterexpression("end", _("Job's date of printing")) 
     318 
     319    parser.add_example('--unit EURO --output /tmp/invoices.pdf start=now-30', 
    320320                       _("This would generate a PDF document containing invoices for all users who have spent some credits last month. Amounts would be in EURO and not VAT information would be included.")) 
    321                         
    322     run(parser, PKInvoice)                    
     321 
     322    run(parser, PKInvoice) 
  • pykota/trunk/bin/pknotify

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    3030try : 
    3131    import PAM 
    32 except ImportError :     
     32except ImportError : 
    3333    hasPAM = False 
    34 else :     
     34else : 
    3535    hasPAM = True 
    3636 
     
    5353  -v | --version             Prints pknotify's version number then exits. 
    5454  -h | --help                Prints this message then exits. 
    55    
     55 
    5656  -d | --destination h[:p]   Sets the destination hostname and optional 
    5757                             port onto which contact the remote PyKotIcon 
    5858                             application. This option is mandatory. 
    5959                             When not specified, the port defaults to 7654. 
    60                               
     60 
    6161  -a | --ask                 Tells pknotify to ask something to the end 
    6262                             user. Then pknotify will output the result. 
    63                         
     63 
    6464  -C | --checkauth           When --ask is used and both an 'username' and a 
    6565                             'password' are asked to the end user, then 
     
    7070                             be printed. If the user is authenticated, then 
    7171                             "USERNAME=xxxx" will be printed as well. 
    72                               
    73   -c | --confirm             Tells pknotify to ask for either a confirmation                        
     72 
     73  -c | --confirm             Tells pknotify to ask for either a confirmation 
    7474                             or abortion. 
    75                               
    76   -D | --denyafter N         With --checkauth above, makes pknotify loop                            
     75 
     76  -D | --denyafter N         With --checkauth above, makes pknotify loop 
    7777                             up to N times if the password is incorrect. 
    7878                             After having reached the limit, "DENY" will 
     
    8080                             The default value of N is 1, meaning the job 
    8181                             is denied after the first unsuccessful try. 
    82                               
     82 
    8383  -N | --noremote action     If it's impossible to connect to the remote 
    8484                             PyKotIcon machine, do this action instead. 
    85                              Allowed actions are 'CONTINUE' and 'CANCEL',  
     85                             Allowed actions are 'CONTINUE' and 'CANCEL', 
    8686                             which will respectively allow the processing 
    8787                             of the print job to continue, or the job to 
    8888                             be cancelled. The default value is CANCEL. 
    89                               
     89 
    9090  -n | --notify              Tells pknotify to send an informational message 
    9191                             to the end user. 
    92                               
     92 
    9393  -q | --quit                Tells pknotify to send a message asking the 
    9494                             PyKotIcon application to exit. This option can 
    9595                             be combined with the other ones to make PyKotIcon 
    9696                             exit after having sent the answer from the dialog. 
    97                               
     97 
    9898  -t | --timeout T           Tells pknotify to ignore the end user's answer if 
    9999                             it comes past T seconds after the dialog box being 
    100                              opened. The default value is 0 seconds, which  
     100                             opened. The default value is 0 seconds, which 
    101101                             tells pknotify to wait indefinitely. 
    102102                             Use this option to avoid having an user who 
    103103                             leaved his computer stall a whole print queue. 
    104                               
     104 
    105105  You MUST specify either --ask, --confirm, --notify or --quit. 
    106106 
    107   arguments :              
    108    
     107  arguments : 
     108 
    109109    -a | --ask : Several arguments are accepted, of the form 
    110110                 "label:varname:defaultvalue". The result will 
     
    119119                 it is not printed, it will be used to check if 
    120120                 authentication is valid if you specify --checkauth. 
    121                   
     121 
    122122    -c | --confirm : A single argument is expected, representing the 
    123123                     message to display. If the dialog is confirmed 
    124124                     then pknotify will print OK, else CANCEL. 
    125                       
    126     -n | --notify : A single argument is expected, representing the                  
     125 
     126    -n | --notify : A single argument is expected, representing the 
    127127                    message to display. In this case pknotify will 
    128128                    always print OK. 
    129                      
    130 examples :                     
     129 
     130examples : 
    131131 
    132132  pknotify -d client:7654 --noremote CONTINUE --confirm "This job costs 10 credits" 
    133    
     133 
    134134  Would display the cost of the print job and asks for confirmation. 
    135135  If the end user doesn't have PyKotIcon running and accepting connections 
    136136  from the print server, PyKota will consider that the end user accepted 
    137137  to print this job. 
    138    
     138 
    139139  pknotify --destination $PYKOTAJOBORIGINATINGHOSTNAME:7654 \\ 
    140140           --checkauth --ask "Your name:username:" "Your password:password:" 
    141             
    142   Asks an username and password, and checks if they are valid.          
     141 
     142  Asks an username and password, and checks if they are valid. 
    143143  NB : The PYKOTAJOBORIGINATINGHOSTNAME environment variable is 
    144144  only set if you launch pknotify from cupspykota through a directive 
    145145  in ~pykota/pykota.conf 
    146    
     146 
    147147  The TCP port you'll use must be reachable on the client from the 
    148148  print server. 
    149149""") 
    150          
    151 class TimeoutError(Exception) :         
     150 
     151class TimeoutError(Exception) : 
    152152    """An exception for timeouts.""" 
    153153    def __init__(self, message = ""): 
     
    157157        return self.message 
    158158    __str__ = __repr__ 
    159      
    160 class PyKotaNotify(Tool) :         
     159 
     160class PyKotaNotify(Tool) : 
    161161    """A class for pknotify.""" 
    162162    def UTF8ToUserCharset(self, text) : 
     
    164164        if text is None : 
    165165            return None 
    166         else :     
    167             return text.decode("UTF-8", "replace").encode(self.charset, "replace")  
    168          
     166        else : 
     167            return text.decode("UTF-8", "replace").encode(self.charset, "replace") 
     168 
    169169    def userCharsetToUTF8(self, text) : 
    170170        """Converts from user's charset to UTF-8.""" 
    171171        if text is None : 
    172172            return None 
    173         else :     
    174             return text.decode(self.charset, "replace").encode("UTF-8", "replace")     
    175          
     173        else : 
     174            return text.decode(self.charset, "replace").encode("UTF-8", "replace") 
     175 
    176176    def sanitizeMessage(self, msg) : 
    177177        """Replaces \\n and returns a messagee in xmlrpclib Binary format.""" 
    178178        return xmlrpclib.Binary(self.userCharsetToUTF8(msg.replace("\\n", "\n"))) 
    179          
     179 
    180180    def convPAM(self, auth, queries=[], userdata=None) : 
    181181        """Prepares PAM datas.""" 
     
    191191        return response 
    192192 
    193     def checkAuth(self, username, password) :     
     193    def checkAuth(self, username, password) : 
    194194        """Checks if we could authenticate an username with a password.""" 
    195         if not hasPAM :     
     195        if not hasPAM : 
    196196            raise PyKotaToolError, _("You MUST install PyPAM for this functionnality to work !") 
    197         else :     
     197        else : 
    198198            retcode = False 
    199199            self.password = password 
     
    213213                retcode = True 
    214214            return retcode 
    215              
    216     def alarmHandler(self, signum, frame) :         
     215 
     216    def alarmHandler(self, signum, frame) : 
    217217        """Alarm handler.""" 
    218218        raise TimeoutError, _("The end user at %s:%i didn't answer within %i seconds. The print job will be cancelled.") % (self.destination, self.port, self.timeout) 
    219          
     219 
    220220    def main(self, arguments, options) : 
    221221        """Notifies or asks questions to end users through PyKotIcon.""" 
     
    226226            self.destination = options["destination"] 
    227227            self.port = 7654 
    228              
     228 
    229229        try : 
    230230            denyafter = int(options["denyafter"]) 
    231231            if denyafter < 1 : 
    232232                raise ValueError 
    233         except (ValueError, TypeError) :         
     233        except (ValueError, TypeError) : 
    234234            denyafter = 1 
    235              
    236         try :     
     235 
     236        try : 
    237237            self.timeout = int(options["timeout"]) 
    238238            if self.timeout < 0 : 
     
    240240        except (ValueError, TypeError) : 
    241241            self.timeout = 0 
    242              
     242 
    243243        if self.timeout : 
    244244            signal.signal(signal.SIGALRM, self.alarmHandler) 
    245245            signal.alarm(self.timeout) 
    246              
    247         try :     
    248             try :     
     246 
     247        try : 
     248            try : 
    249249                server = xmlrpclib.ServerProxy("http://%s:%s" % (self.destination, self.port)) 
    250250                if options["ask"] : 
     
    253253                        if denyafter < 1 : 
    254254                            raise ValueError 
    255                     except (ValueError, TypeError) :     
     255                    except (ValueError, TypeError) : 
    256256                        denyafter = 1 
    257257                    labels = [] 
     
    261261                        try : 
    262262                            (label, varname, varvalue) = arg.split(":", 2) 
    263                         except ValueError :     
     263                        except ValueError : 
    264264                            raise PyKotaCommandLineError, "argument '%s' is invalid !" % arg 
    265265                        labels.append(self.sanitizeMessage(label)) 
     
    267267                        varnames.append(varname) 
    268268                        varvalues[varname] = self.sanitizeMessage(varvalue) 
    269                          
    270                     passnumber = 1     
     269 
     270                    passnumber = 1 
    271271                    authok = None 
    272272                    while (authok != "AUTH=YES") and (passnumber <= denyafter) : 
    273                         result = server.askDatas(labels, varnames, varvalues)     
     273                        result = server.askDatas(labels, varnames, varvalues) 
    274274                        if not options["checkauth"] : 
    275275                            break 
    276276                        if result["isValid"] : 
    277277                            if ("username" in varnames) and ("password" in varnames) : 
    278                                 if self.checkAuth(self.UTF8ToUserCharset(result["username"].data[:]),  
     278                                if self.checkAuth(self.UTF8ToUserCharset(result["username"].data[:]), 
    279279                                                  self.UTF8ToUserCharset(result["password"].data[:])) : 
    280280                                    authok = "AUTH=YES" 
    281                                 else :     
     281                                else : 
    282282                                    authok = "AUTH=NO" 
    283                             else :         
    284                                 authok = "AUTH=IMPOSSIBLE"         
    285                         passnumber += 1         
    286                                      
     283                            else : 
     284                                authok = "AUTH=IMPOSSIBLE" 
     285                        passnumber += 1 
     286 
    287287                    if options["checkauth"] and options["denyafter"] \ 
    288288                       and (passnumber > denyafter) \ 
     
    294294                               and ((varname != "username") or (authok in (None, "AUTH=YES"))) : 
    295295                                print "%s=%s" % (varname.upper(), self.UTF8ToUserCharset(result[varname].data[:])) 
    296                         if authok is not None :         
    297                             print authok         
     296                        if authok is not None : 
     297                            print authok 
    298298                elif options["confirm"] : 
    299299                    print server.showDialog(self.sanitizeMessage(arguments[0]), True) 
    300300                elif options["notify"] : 
    301301                    print server.showDialog(self.sanitizeMessage(arguments[0]), False) 
    302                      
    303                 if options["quit"] :     
     302 
     303                if options["quit"] : 
    304304                    server.quitApplication() 
    305305            except (xmlrpclib.ProtocolError, socket.error, socket.gaierror), msg : 
     
    309309                #except (AttributeError, IndexError) : 
    310310                #    pass 
    311                 #else :     
     311                #else : 
    312312                #    if errnum == errno.ECONNREFUSED : 
    313313                #        raise PyKotaToolError, "%s : %s" % (str(msg), (_("Are you sure that PyKotIcon is running and accepting incoming connections on %s:%s ?") % (self.destination, self.port))) 
    314314                self.printInfo("%s : %s" % (_("Connection error"), str(msg)), "warn") 
    315             except TimeoutError, msg :     
     315            except TimeoutError, msg : 
    316316                self.printInfo(msg, "warn") 
    317317                print "CANCEL"      # Timeout occured : job is cancelled. 
    318         finally :     
    319             if self.timeout :     
    320                 signal.alarm(0)     
    321          
     318        finally : 
     319            if self.timeout : 
     320                signal.alarm(0) 
     321 
    322322if __name__ == "__main__" : 
    323323    retcode = 0 
     
    331331                        "timeout=", "ask", "checkauth", "confirm", "notify", \ 
    332332                        "quit", "noremote=" ] 
    333          
     333 
    334334        # Initializes the command line tool 
    335335        notifier = PyKotaNotify(doc=__doc__) 
    336336        notifier.deferredInit() 
    337          
     337 
    338338        # parse and checks the command line 
    339339        (options, args) = notifier.parseCommandline(sys.argv[1:], short_options, long_options) 
    340          
     340 
    341341        # sets long options 
    342342        options["help"] = options["h"] or options["help"] 
     
    351351        options["timeout"] = options["t"] or options["timeout"] or defaults["timeout"] 
    352352        options["noremote"] = (options["N"] or options["noremote"] or defaults["noremote"]).upper() 
    353          
     353 
    354354        if options["help"] : 
    355355            notifier.display_usage_and_quit() 
     
    370370        else : 
    371371            retcode = notifier.main(args, options) 
    372     except KeyboardInterrupt :         
     372    except KeyboardInterrupt : 
    373373        logerr("\nInterrupted with Ctrl+C !\n") 
    374374        retcode = -3 
    375     except PyKotaCommandLineError, msg :     
     375    except PyKotaCommandLineError, msg : 
    376376        logerr("%s : %s\n" % (sys.argv[0], msg)) 
    377377        print "CANCEL"  # Forces the cancellation of the print job if a command line switch is incorrect 
    378378        retcode = -2 
    379     except SystemExit :         
     379    except SystemExit : 
    380380        pass 
    381381    except : 
    382382        try : 
    383383            notifier.crashed("%s failed" % sys.argv[0]) 
    384         except :     
     384        except : 
    385385            crashed("%s failed" % sys.argv[0]) 
    386386        retcode = -1 
    387          
    388     sys.exit(retcode)     
     387 
     388    sys.exit(retcode) 
  • pykota/trunk/bin/pkprinters

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    4747  -v | --version       Prints pkprinters's version number then exits. 
    4848  -h | --help          Prints this message then exits. 
    49    
    50   -a | --add           Adds printers if they don't exist on the Quota  
     49 
     50  -a | --add           Adds printers if they don't exist on the Quota 
    5151                       Storage Server. If they exist, they are modified 
    5252                       unless -s|--skipexisting is also used. 
    53                         
     53 
    5454  -d | --delete        Deletes printers from the quota storage. 
    55    
     55 
    5656  -D | --description d Adds a textual description to printers. 
    5757 
     
    6262                       If both are to be set, separate them with a comma. 
    6363                       Floating point and negative values are allowed. 
    64    
    65   -g | --groups pg1[,pg2...] Adds or Remove the printer(s) to the printer  
     64 
     65  -g | --groups pg1[,pg2...] Adds or Remove the printer(s) to the printer 
    6666                       groups pg1, pg2, etc... which must already exist. 
    6767                       A printer group is just like a normal printer, 
    6868                       only that it is usually unknown from the printing 
    6969                       system. Create printer groups exactly the same 
    70                        way that you create printers, then add other  
     70                       way that you create printers, then add other 
    7171                       printers to them with this option. 
    7272                       Accounting is done on a printer and on all 
     
    7474                       is done on a printer and on all the printer groups 
    7575                       it belongs to. 
    76                        If the --remove option below is not used, the  
     76                       If the --remove option below is not used, the 
    7777                       default action is to add printers to the specified 
    7878                       printer groups. 
    79                         
     79 
    8080  -l | --list          List informations about the printer(s) and the 
    8181                       printers groups it is a member of. 
    82                         
    83   -r | --remove        In combination with the --groups option above,                        
     82 
     83  -r | --remove        In combination with the --groups option above, 
    8484                       remove printers from the specified printers groups. 
    85                         
     85 
    8686  -s | --skipexisting  In combination with the --add option above, tells 
    8787                       pkprinters to not modify existing printers. 
    88                         
     88 
    8989  -m | --maxjobsize s  Sets the maximum job size allowed on the printer 
    9090                       to s pages. 
    91                         
     91 
    9292  -p | --passthrough   Activate passthrough mode for the printer. In this 
    9393                       mode, users are allowed to print without any impact 
    9494                       on their quota or account balance. 
    95                         
     95 
    9696  -n | --nopassthrough Deactivate passthrough mode for the printer. 
    97                        Without -p or -n, printers are created in  
     97                       Without -p or -n, printers are created in 
    9898                       normal mode, i.e. no passthrough. 
    99    
    100   printer1 through printerN can contain wildcards if the --add option  
     99 
     100  printer1 through printerN can contain wildcards if the --add option 
    101101  is not set. 
    102    
    103 examples :                               
     102 
     103examples : 
    104104 
    105105  $ pkprinters --add -D "HP Printer" --charge 0.05,0.1 hp2100 hp2200 hp8000 
    106    
     106 
    107107  Will create three printers named hp2100, hp2200 and hp8000. 
    108108  Their price per page will be set at 0.05 unit, and their price 
     
    110110  or whatever you want them to mean. 
    111111  All of their descriptions will be set to the string "HP Printer". 
    112   If any of these printers already exists, it will also be modified  
     112  If any of these printers already exists, it will also be modified 
    113113  unless the -s|--skipexisting command line option is also used. 
    114              
     114 
    115115  $ pkprinters --delete "*" 
    116    
     116 
    117117  This will completely delete all printers and associated quota information, 
    118118  as well as their job history. USE WITH CARE ! 
    119    
     119 
    120120  $ pkprinters --groups Laser,HP "hp*" 
    121    
    122   This will put all printers which name matches "hp*" into printers groups  
     121 
     122  This will put all printers which name matches "hp*" into printers groups 
    123123  Laser and HP, which MUST already exist. 
    124    
     124 
    125125  $ pkprinters --groups LexMark --remove hp2200 
    126    
     126 
    127127  This will remove the hp2200 printer from the LexMark printer group. 
    128128""") 
    129          
    130 class PKPrinters(PyKotaTool) :         
     129 
     130class PKPrinters(PyKotaTool) : 
    131131    """A class for a printers manager.""" 
    132132    def modifyPrinter(self, printer, charges, perpage, perjob, description, passthrough, nopassthrough, maxjobsize) : 
    133133        if charges : 
    134             printer.setPrices(perpage, perjob)     
     134            printer.setPrices(perpage, perjob) 
    135135        if description is not None :        # NB : "" is allowed ! 
    136136            printer.setDescription(description) 
    137         if nopassthrough :     
     137        if nopassthrough : 
    138138            printer.setPassThrough(False) 
    139         if passthrough :     
     139        if passthrough : 
    140140            printer.setPassThrough(True) 
    141141        if maxjobsize is not None : 
    142142            printer.setMaxJobSize(maxjobsize) 
    143              
    144     def managePrintersGroups(self, pgroups, printer, remove) :         
     143 
     144    def managePrintersGroups(self, pgroups, printer, remove) : 
    145145        """Manage printer group membership.""" 
    146146        for pgroup in pgroups : 
     
    148148                pgroup.delPrinterFromGroup(printer) 
    149149            else : 
    150                 pgroup.addPrinterToGroup(printer)     
    151                  
    152     def getPrinterDeviceURI(self, printername) :             
     150                pgroup.addPrinterToGroup(printer) 
     151 
     152    def getPrinterDeviceURI(self, printername) : 
    153153        """Returns the Device URI attribute for a particular printer.""" 
    154154        if not printername : 
     
    159159        try : 
    160160            return cups.doRequest(req).printer["device-uri"][0][1] 
    161         except :     
     161        except : 
    162162            self.printInfo(_("Impossible to retrieve %(printername)s's DeviceURI") % locals(), "warn") 
    163163            return "" 
    164          
     164 
    165165    def isPrinterCaptured(self, printername=None, deviceuri=None) : 
    166166        """Returns True if the printer is already redirected through PyKota's backend, else False.""" 
    167167        if (deviceuri or self.getPrinterDeviceURI(printername)).find("cupspykota:") != -1 : 
    168168            return True 
    169         else :     
     169        else : 
    170170            return False 
    171          
    172     def reroutePrinterThroughPyKota(self, printer) :     
     171 
     172    def reroutePrinterThroughPyKota(self, printer) : 
    173173        """Reroutes a CUPS printer through PyKota.""" 
    174174        uri = self.getPrinterDeviceURI(printer.Name) 
     
    177177             os.system('lpadmin -p "%s" -v "%s"' % (printer.Name, newuri)) 
    178178             self.logdebug("Printer %s rerouted to %s" % (printer.Name, newuri)) 
    179               
    180     def deroutePrinterFromPyKota(self, printer) :     
     179 
     180    def deroutePrinterFromPyKota(self, printer) : 
    181181        """Deroutes a PyKota printer through CUPS only.""" 
    182182        uri = self.getPrinterDeviceURI(printer.Name) 
     
    187187             os.system('lpadmin -p "%s" -v "%s"' % (printer.Name, newuri)) 
    188188             self.logdebug("Printer %s rerouted to %s" % (printer.Name, newuri)) 
    189                                       
     189 
    190190    def main(self, names, options) : 
    191191        """Manage printers.""" 
    192192        if not options["list"] : 
    193193            self.adminOnly() 
    194              
     194 
    195195        docups = options["cups"] 
    196          
    197         if not options["list"] :     
     196 
     197        if not options["list"] : 
    198198            percent = Percent(self) 
    199              
     199 
    200200        if not options["add"] : 
    201201            if not options["list"] : 
     
    208208                    percent.display("\n") 
    209209                raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(names) 
    210             if not options["list"] :     
     210            if not options["list"] : 
    211211                percent.setSize(len(printers)) 
    212                  
     212 
    213213        if options["list"] : 
    214214            for printer in printers : 
     
    220220                print "    %s" % (_("Maximum job size : %s") % ((printer.MaxJobSize and (_("%s pages") % printer.MaxJobSize)) or _("Unlimited"))) 
    221221                print "    %s" % (_("Routed through PyKota : %s") % ((self.isPrinterCaptured(printer.Name) and _("YES")) or _("NO"))) 
    222                 if parents :  
     222                if parents : 
    223223                    print "    %s %s" % (_("in"), parents) 
    224                 print     
    225         elif options["delete"] :     
     224                print 
     225        elif options["delete"] : 
    226226            percent.display("\n%s..." % _("Deletion")) 
    227227            self.storage.deleteManyPrinters(printers) 
     
    233233                    percent.oneMore() 
    234234        else : 
    235             if options["groups"] :         
     235            if options["groups"] : 
    236236                printersgroups = self.storage.getMatchingPrinters(options["groups"]) 
    237237                if not printersgroups : 
    238238                    raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(options["groups"].split(',')) 
    239             else :          
     239            else : 
    240240                printersgroups = [] 
    241                      
     241 
    242242            if options["charge"] : 
    243243                try : 
    244244                    charges = [float(part) for part in options["charge"].split(',', 1)] 
    245                 except ValueError :     
     245                except ValueError : 
    246246                    raise PyKotaCommandLineError, _("Invalid charge amount value %s") % options["charge"] 
    247                 else :     
     247                else : 
    248248                    if len(charges) > 2 : 
    249249                        charges = charges[:2] 
     
    251251                        charges = [charges[0], None] 
    252252                    (perpage, perjob) = charges 
    253             else :         
     253            else : 
    254254                charges = perpage = perjob = None 
    255                      
    256             if options["maxjobsize"] :         
     255 
     256            if options["maxjobsize"] : 
    257257                try : 
    258258                    maxjobsize = int(options["maxjobsize"]) 
    259259                    if maxjobsize < 0 : 
    260260                        raise ValueError 
    261                 except ValueError :     
     261                except ValueError : 
    262262                    raise PyKotaCommandLineError, _("Invalid maximum job size value %s") % options["maxjobsize"] 
    263             else :         
     263            else : 
    264264                maxjobsize = None 
    265                      
     265 
    266266            description = options["description"] 
    267267            if description : 
    268268                description = description.strip() 
    269                  
    270             nopassthrough = options["nopassthrough"]     
     269 
     270            nopassthrough = options["nopassthrough"] 
    271271            passthrough = options["passthrough"] 
    272272            remove = options["remove"] 
     
    274274            self.storage.beginTransaction() 
    275275            try : 
    276                 if options["add"] :     
     276                if options["add"] : 
    277277                    percent.display("%s...\n" % _("Creation")) 
    278278                    percent.setSize(len(names)) 
     
    283283                                           description, passthrough, \ 
    284284                                           nopassthrough, maxjobsize) 
    285                             oldprinter = self.storage.addPrinter(printer)                
    286                              
     285                            oldprinter = self.storage.addPrinter(printer) 
     286 
    287287                            if docups : 
    288288                                 self.reroutePrinterThroughPyKota(printer) 
    289                                       
     289 
    290290                            if oldprinter is not None : 
    291291                                if skipexisting : 
    292292                                    self.logdebug(_("Printer %s already exists, skipping.") % pname) 
    293                                 else :     
     293                                else : 
    294294                                    self.logdebug(_("Printer %s already exists, will be modified.") % pname) 
    295295                                    self.modifyPrinter(oldprinter, charges, \ 
     
    297297                                               passthrough, nopassthrough, \ 
    298298                                               maxjobsize) 
    299                                     oldprinter.save()            
     299                                    oldprinter.save() 
    300300                                    self.managePrintersGroups(printersgroups, oldprinter, remove) 
    301                             elif printersgroups :         
     301                            elif printersgroups : 
    302302                                self.managePrintersGroups(printersgroups, \ 
    303303                                                          self.storage.getPrinter(pname), \ 
    304304                                                          remove) 
    305                         else :     
     305                        else : 
    306306                            raise PyKotaCommandLineError, _("Invalid printer name %s") % pname 
    307307                        percent.oneMore() 
    308                 else :         
     308                else : 
    309309                    percent.display("\n%s...\n" % _("Modification")) 
    310                     for printer in printers :         
     310                    for printer in printers : 
    311311                        self.modifyPrinter(printer, charges, perpage, perjob, \ 
    312312                                           description, passthrough, \ 
    313313                                           nopassthrough, maxjobsize) 
    314                         printer.save()     
     314                        printer.save() 
    315315                        self.managePrintersGroups(printersgroups, printer, remove) 
    316316                        if docups : 
    317317                            self.reroutePrinterThroughPyKota(printer) 
    318318                        percent.oneMore() 
    319             except :                     
     319            except : 
    320320                self.storage.rollbackTransaction() 
    321321                raise 
    322             else :     
     322            else : 
    323323                self.storage.commitTransaction() 
    324                  
     324 
    325325        if not options["list"] : 
    326326            percent.done() 
    327                       
    328 if __name__ == "__main__" :  
     327 
     328if __name__ == "__main__" : 
    329329    retcode = 0 
    330330    try : 
     
    334334                        "skipexisting", "passthrough", "nopassthrough", \ 
    335335                        "maxjobsize="] 
    336          
     336 
    337337        # Initializes the command line tool 
    338338        manager = PKPrinters(doc=__doc__) 
    339339        manager.deferredInit() 
    340          
     340 
    341341        # parse and checks the command line 
    342342        (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options) 
    343          
     343 
    344344        # sets long options 
    345345        options["help"] = options["h"] or options["help"] 
     
    349349        options["charge"] = options["c"] or options["charge"] 
    350350        options["description"] = options["D"] or options["description"] 
    351         options["delete"] = options["d"] or options["delete"]  
     351        options["delete"] = options["d"] or options["delete"] 
    352352        options["groups"] = options["g"] or options["groups"] 
    353353        options["list"] = options["l"] or options["list"] 
     
    357357        options["passthrough"] = options["p"] or options["passthrough"] 
    358358        options["nopassthrough"] = options["n"] or options["nopassthrough"] 
    359          
     359 
    360360        if options["help"] : 
    361361            manager.display_usage_and_quit() 
     
    374374        else : 
    375375            retcode = manager.main(args, options) 
    376     except KeyboardInterrupt :         
     376    except KeyboardInterrupt : 
    377377        logerr("\nInterrupted with Ctrl+C !\n") 
    378378        retcode = -3 
    379     except PyKotaCommandLineError, msg :     
     379    except PyKotaCommandLineError, msg : 
    380380        logerr("%s : %s\n" % (sys.argv[0], msg)) 
    381381        retcode = -2 
    382     except SystemExit :         
     382    except SystemExit : 
    383383        pass 
    384384    except : 
    385385        try : 
    386386            manager.crashed("pkprinters failed") 
    387         except :     
     387        except : 
    388388            crashed("pkprinters failed") 
    389389        retcode = -1 
     
    391391    try : 
    392392        manager.storage.close() 
    393     except (TypeError, NameError, AttributeError) :     
     393    except (TypeError, NameError, AttributeError) : 
    394394        pass 
    395          
    396     sys.exit(retcode)     
     395 
     396    sys.exit(retcode) 
  • pykota/trunk/bin/pkrefund

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    3434    from reportlab.lib import pagesizes 
    3535    from reportlab.lib.units import cm 
    36 except ImportError :     
     36except ImportError : 
    3737    hasRL = False 
    38 else :     
     38else : 
    3939    hasRL = True 
    40      
     40 
    4141try : 
    42     import PIL.Image  
    43 except ImportError :     
     42    import PIL.Image 
     43except ImportError : 
    4444    hasPIL = False 
    45 else :     
     45else : 
    4646    hasPIL = True 
    4747 
     
    5454from pykota.errors import PyKotaToolError, PyKotaCommandLineError 
    5555from pykota.tool import Percent, PyKotaTool 
    56          
    57 class PKRefund(PyKotaTool) :         
     56 
     57class PKRefund(PyKotaTool) : 
    5858    """A class for refund manager.""" 
    5959    validfilterkeys = [ "username", 
     
    6565                        "end", 
    6666                      ] 
    67                        
     67 
    6868    def printVar(self, label, value, size) : 
    6969        """Outputs a variable onto the PDF canvas. 
    70          
     70 
    7171           Returns the number of points to substract to current Y coordinate. 
    72         """    
     72        """ 
    7373        xcenter = (self.pagesize[0] / 2.0) - 1*cm 
    7474        self.canvas.saveState() 
     
    8181        self.canvas.restoreState() 
    8282        self.ypos -= (size + 4) 
    83          
     83 
    8484    def pagePDF(self, receiptnumber, name, values, unit, reason) : 
    8585        """Generates a new page in the PDF document.""" 
     
    9292            datetime = time.strftime("%c", time.localtime()).decode(self.charset, "replace") 
    9393            self.printVar(_("Edited on"), datetime, 14) 
    94                  
     94 
    9595            self.ypos -= 20 
    9696            self.printVar(_("Jobs refunded"), str(values["nbjobs"]), 18) 
     
    101101            self.canvas.showPage() 
    102102            return 1 
    103         return 0     
    104          
     103        return 0 
     104 
    105105    def initPDF(self, logo) : 
    106106        """Initializes the PDF document.""" 
    107         self.pdfDocument = cStringIO.StringIO()         
     107        self.pdfDocument = cStringIO.StringIO() 
    108108        self.canvas = c = canvas.Canvas(self.pdfDocument, \ 
    109109                                        pagesize=self.pagesize, \ 
    110110                                        pageCompression=1) 
    111          
     111 
    112112        c.setAuthor(self.effectiveUserName) 
    113113        c.setTitle(_("PyKota print job refunding receipts")) 
    114114        c.setSubject(_("Print job refunding receipts generated with PyKota")) 
    115          
     115 
    116116        self.canvas.beginForm("background") 
    117117        self.canvas.saveState() 
    118          
    119         self.ypos = self.pagesize[1] - (2 * cm)             
    120          
     118 
     119        self.ypos = self.pagesize[1] - (2 * cm) 
     120 
    121121        xcenter = self.pagesize[0] / 2.0 
    122122        if logo : 
    123             try :     
     123            try : 
    124124                imglogo = PIL.Image.open(logo) 
    125             except IOError :     
     125            except IOError : 
    126126                self.printInfo("Unable to open image %s" % logo, "warn") 
    127127            else : 
    128128                (width, height) = imglogo.size 
    129                 multi = float(width) / (8 * cm)  
     129                multi = float(width) / (8 * cm) 
    130130                width = float(width) / multi 
    131131                height = float(height) / multi 
     
    134134                                  self.ypos, \ 
    135135                                  width, height) 
    136          
     136 
    137137        self.ypos -= (cm + 20) 
    138138        self.canvas.setFont("Helvetica-Bold", 14) 
     
    140140        msg = _("Here's the receipt for the refunding of your print jobs") 
    141141        self.canvas.drawCentredString(xcenter, self.ypos, "%s :" % msg) 
    142          
     142 
    143143        self.yorigine = self.ypos 
    144144        self.canvas.restoreState() 
    145145        self.canvas.endForm() 
    146          
    147     def endPDF(self, fname) :     
     146 
     147    def endPDF(self, fname) : 
    148148        """Flushes the PDF generator.""" 
    149149        self.canvas.save() 
    150         if fname != "-" :         
     150        if fname != "-" : 
    151151            outfile = open(fname, "w") 
    152152            outfile.write(self.pdfDocument.getvalue()) 
    153153            outfile.close() 
    154         else :     
     154        else : 
    155155            sys.stdout.write(self.pdfDocument.getvalue()) 
    156156            sys.stdout.flush() 
    157          
     157 
    158158    def genReceipts(self, peruser, logo, outfname, firstnumber, reason, unit) : 
    159159        """Generates the receipts file.""" 
     
    162162            if outfname != "-" : 
    163163                percent.display("%s...\n" % _("Generating receipts")) 
    164                  
     164 
    165165            self.initPDF(logo) 
    166166            number = firstnumber 
     
    169169                if outfname != "-" : 
    170170                    percent.oneMore() 
    171                      
     171 
    172172            if number > firstnumber : 
    173173                self.endPDF(outfname) 
    174                  
     174 
    175175            if outfname != "-" : 
    176176                percent.done() 
    177          
     177 
    178178    def main(self, arguments, options) : 
    179179        """Refunds jobs.""" 
     
    182182        if not hasPIL : 
    183183            raise PyKotaToolError, "The Python Imaging Library is missing. Download it from http://www.pythonware.com/downloads" 
    184              
    185         self.adminOnly()     
    186              
     184 
     185        self.adminOnly() 
     186 
    187187        self.pagesize = getPageSize(options.pagesize) 
    188          
     188 
    189189        if (not options.reason) or (not options.reason.strip()) : 
    190190            raise PyKotaCommandLineError, _("Refunding for no reason is forbidden. Please use the --reason command line option.") 
    191              
     191 
    192192        extractonly = {} 
    193193        for filterexp in arguments : 
     
    197197                    filterkey = filterkey.lower() 
    198198                    if filterkey not in self.validfilterkeys : 
    199                         raise ValueError                 
    200                 except ValueError :     
     199                        raise ValueError 
     200                except ValueError : 
    201201                    raise PyKotaCommandLineError, _("Invalid filter value [%s], see help.") % filterexp 
    202                 else :     
     202                else : 
    203203                    extractonly.update({ filterkey : filtervalue }) 
    204              
     204 
    205205        percent = Percent(self) 
    206206        outfname = options.output.strip().encode(sys.getfilesystemencoding()) 
    207207        if outfname != "-" : 
    208208            percent.display("%s..." % _("Extracting datas")) 
    209         else :     
     209        else : 
    210210            options.force = True 
    211211            self.printInfo(_("The PDF file containing the receipts will be sent to stdout. --force is assumed."), "warn") 
    212              
    213         username = extractonly.get("username")     
     212 
     213        username = extractonly.get("username") 
    214214        if username : 
    215215            user = self.storage.getUser(username) 
    216216        else : 
    217217            user = None 
    218              
    219         printername = extractonly.get("printername")     
     218 
     219        printername = extractonly.get("printername") 
    220220        if printername : 
    221221            printer = self.storage.getPrinter(printername) 
    222         else :     
     222        else : 
    223223            printer = None 
    224              
     224 
    225225        start = extractonly.get("start") 
    226226        end = extractonly.get("end") 
    227227        (start, end) = self.storage.cleanDates(start, end) 
    228          
    229         jobs = self.storage.retrieveHistory(user=user,     
    230                                             printer=printer,  
     228 
     229        jobs = self.storage.retrieveHistory(user=user, 
     230                                            printer=printer, 
    231231                                            hostname=extractonly.get("hostname"), 
    232232                                            billingcode=extractonly.get("billingcode"), 
     
    235235                                            end=end, 
    236236                                            limit=0) 
    237                                              
    238         peruser = {}                                     
    239         nbjobs = 0                                     
    240         nbpages = 0                                             
     237 
     238        peruser = {} 
     239        nbjobs = 0 
     240        nbpages = 0 
    241241        nbcredits = 0.0 
    242242        percent.setSize(len(jobs)) 
    243243        if outfname != "-" : 
    244244            percent.display("\n") 
    245         for job in jobs :                                     
     245        for job in jobs : 
    246246            if job.JobSize and (job.JobAction not in ("DENY", "CANCEL", "REFUND")) : 
    247247                if options.force : 
     
    256256                    if outfname != "-" : 
    257257                        percent.oneMore() 
    258                 else :     
     258                else : 
    259259                    print _("Date : %s") % str(job.JobDate)[:19] 
    260260                    print _("Printer : %s") % job.PrinterName 
     
    266266                    print _("Pages : %i") % job.JobSize 
    267267                    print _("Credits : %.3f") % job.JobPrice 
    268                      
    269                     while True :                              
     268 
     269                    while True : 
    270270                        answer = raw_input("\t%s ? " % _("Refund (Y/N)")).strip().upper() 
    271271                        if answer == _("Y") : 
     
    278278                            counters["nbjobs"] += 1 
    279279                            nbjobs += 1 
    280                             break     
    281                         elif answer == _("N") :     
    282280                            break 
    283                     print         
     281                        elif answer == _("N") : 
     282                            break 
     283                    print 
    284284        if outfname != "-" : 
    285285            percent.done() 
    286         self.genReceipts(peruser,  
    287                          options.logo.strip().encode(sys.getfilesystemencoding()),  
    288                          outfname,  
    289                          options.number,  
    290                          options.reason,  
     286        self.genReceipts(peruser, 
     287                         options.logo.strip().encode(sys.getfilesystemencoding()), 
     288                         outfname, 
     289                         options.number, 
     290                         options.reason, 
    291291                         options.unit or _("Credits")) 
    292         if outfname != "-" :     
     292        if outfname != "-" : 
    293293            nbusers = len(peruser) 
    294294            print _("Refunded %(nbusers)i users for %(nbjobs)i jobs, %(nbpages)i pages and %(nbcredits).3f credits") \ 
    295295                     % locals() 
    296              
    297 if __name__ == "__main__" :  
     296 
     297if __name__ == "__main__" : 
    298298    parser = PyKotaOptionParser(description=_("Refunding tool for PyKota."), 
    299299                                usage="pkrefund [options] [filterexpr]") 
     
    313313                            default=u"A4", 
    314314                            help=_("Set the size of the page. Most well known page sizes are recognized, like 'A4' or 'Letter' to name a few. The default page size is %default.")) 
    315     parser.add_option("-n", "--number",                         
     315    parser.add_option("-n", "--number", 
    316316                            dest="number", 
    317317                            type="int", 
     
    329329                            type="string", 
    330330                            help=_("The reason why there was a refund.")) 
    331                              
     331 
    332332    # TODO : due to Python's optparse.py bug #1498146 fixed in rev 46861 
    333333    # TODO : we can't use 'default=_("Credits")' for this option 
    334     parser.add_option("-u", "--unit",                    
     334    parser.add_option("-u", "--unit", 
    335335                            dest="unit", 
    336336                            type="string", 
    337337                            help=_("The name of the unit to use on the receipts. The default value is 'Credits' or its locale translation.")) 
    338                              
     338 
    339339    parser.add_filterexpression("username", _("User's name")) 
    340340    parser.add_filterexpression("printername", _("Printer's name")) 
     
    344344    parser.add_filterexpression("start", _("Job's date of printing")) 
    345345    parser.add_filterexpression("end", _("Job's date of printing")) 
    346      
     346 
    347347    parser.add_example('--output /tmp/receipts.pdf jobid=503', 
    348348                       _("This would refund all jobs which Id is 503. A confirmation would be asked for each job to refund, and a PDF file named /tmp/receipts.pdf would be created containing printable receipts. BEWARE of job ids rolling over if you reset CUPS' history.")) 
    349    
     349 
    350350    parser.add_example('--reason "Hardware problem" jobid=503 start=today-7', 
    351351                       _("This would refund all jobs which id is 503 but which would have been printed during the  past week. The reason would be marked as being an hardware problem.")) 
    352    
     352 
    353353    parser.add_example('--force username=jerome printername=HP2100', 
    354354                       _("This would refund all jobs printed by user jerome on printer HP2100. No confirmation would be asked.")) 
    355    
     355 
    356356    parser.add_example('--force printername=HP2100 start=200602 end=yesterday', 
    357357                       _("This would refund all jobs printed on printer HP2100 between February 1st 2006 and yesterday. No confirmation would be asked.")) 
    358      
     358 
    359359    (options, arguments) = parser.parse_args() 
    360     run(parser, PKRefund)                    
     360    run(parser, PKRefund) 
  • pykota/trunk/bin/pksetup

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    6060# IMPORTANT : many more directives can be used, and some of the directives 
    6161# below accept different and/or more complex parameters. Please read 
    62 # /usr/share/pykota/conf/pykota.conf.sample for more details about the  
     62# /usr/share/pykota/conf/pykota.conf.sample for more details about the 
    6363# numerous possibilities allowed. 
    6464# 
     
    122122onaccountererror : stop 
    123123 
    124 # Who will receive warning messages ?  
     124# Who will receive warning messages ? 
    125125# both means admin and user. 
    126126mailto : both 
     
    136136poorman : 1.0 
    137137 
    138 # Warning messages to use  
    139 poorwarn : Your Print Quota account balance is low.  
     138# Warning messages to use 
     139poorwarn : Your Print Quota account balance is low. 
    140140 Soon you'll not be allowed to print anymore. 
    141141 
    142142softwarn : Your Print Quota Soft Limit is reached. 
    143143 This means that you may still be allowed to print for some 
    144  time, but you must contact your administrator to purchase  
     144 time, but you must contact your administrator to purchase 
    145145 more print quota. 
    146146 
     
    150150 as soon as possible to solve the problem. 
    151151 
    152 # Number of banners allowed to be printed by users  
     152# Number of banners allowed to be printed by users 
    153153# who are over quota 
    154154maxdenybanners : 0 
     
    181181    cupsrestart = "/etc/init.d/cupsys restart"  # overload it if needed 
    182182    adduser = "adduser --system --group --home /etc/pykota --gecos PyKota pykota" # overload it if needed 
    183     packages = [ "wget",  
    184                  "bzip2",  
    185                  "subversion",  
    186                  "postgresql",  
     183    packages = [ "wget", 
     184                 "bzip2", 
     185                 "subversion", 
     186                 "postgresql", 
    187187                 "postgresql-client", 
    188188                 "cupsys", 
     
    200200                 "python-pam", 
    201201                 "pkpgcounter" ] 
    202          
     202 
    203203    otherpackages = [ 
    204204                      { "name" : "pkipplib", 
     
    219219                                     ], 
    220220                      }, 
    221                    ]   
    222                     
    223     def __init__(self) :                
     221                   ] 
     222 
     223    def __init__(self) : 
    224224        """Initializes instance specific datas.""" 
    225225        self.launched = [] 
    226          
    227     def yesno(self, message) :         
     226 
     227    def yesno(self, message) : 
    228228        """Asks the end user some question and returns the answer.""" 
    229229        try : 
    230230            return raw_input("\n%s ? " % message).strip().upper()[0] == 'Y' 
    231         except IndexError :     
     231        except IndexError : 
    232232            return False 
    233          
    234     def confirmCommand(self, message, command, record=True) :     
     233 
     234    def confirmCommand(self, message, command, record=True) : 
    235235        """Asks for confirmation before a command is launched, and launches it if needed.""" 
    236236        if self.yesno("The following command will be launched %(message)s :\n%(command)s\nDo you agree" % locals()) : 
     
    241241        else : 
    242242            return False 
    243              
    244     def confirmPipe(self, message, command) :     
     243 
     244    def confirmPipe(self, message, command) : 
    245245        """Asks for confirmation before a command is launched in a pipe, launches it if needed, and returns the result.""" 
    246246        if self.yesno("The following command will be launched %(message)s :\n%(command)s\nDo you agree" % locals()) : 
     
    251251        else : 
    252252            return False 
    253              
     253 
    254254    def listPrinters(self) : 
    255255        """Returns a list of tuples (queuename, deviceuri) for all existing print queues.""" 
     
    263263            queuename = begin.split()[-1] 
    264264            printers.append((queuename, deviceuri)) 
    265         return printers     
    266          
    267     def downloadOtherPackages(self) :     
     265        return printers 
     266 
     267    def downloadOtherPackages(self) : 
    268268        """Downloads and install additional packages from http://www.pykota.com or other websites""" 
    269269        olddirectory = os.getcwd() 
     
    278278            if url.startswith("svn://") : 
    279279                download = 'svn export "%(url)s" %(name)s' % locals() 
    280             else :     
     280            else : 
    281281                download = 'wget --user-agent=pksetup "%(url)s"' % locals() 
    282282            if self.confirmCommand("to download %(name)s" % locals(), download) : 
    283283                self.confirmCommand("to install %(name)s" % locals(), commands) 
    284         self.confirmCommand("to remove the temporary directory %(directory)s" % locals(),      
     284        self.confirmCommand("to remove the temporary directory %(directory)s" % locals(), 
    285285                            "rm -fr %(directory)s" % locals(), 
    286286                            record=False) 
    287         os.chdir(olddirectory)     
    288          
     287        os.chdir(olddirectory) 
     288 
    289289    def waitPrintersOnline(self) : 
    290290        """Asks the admin to switch all printers ON.""" 
    291291        while not self.yesno("First you MUST switch ALL your printers ON. Are ALL your printers ON") : 
    292292            pass 
    293              
     293 
    294294    def setupDatabase(self) : 
    295295        """Creates the database.""" 
    296296        pykotadirectory = self.pykotadirectory 
    297297        self.confirmCommand("to create PyKota's database in PostgreSQL", 'su - postgres -c "psql -f %(pykotadirectory)s/postgresql/pykota-postgresql.sql template1"' % locals()) 
    298          
     298 
    299299    def configurePostgreSQL(self) : 
    300300        """Configures PostgreSQL for PyKota to work.""" 
     
    314314                        if tcpip is False : 
    315315                            tcpip = answer.startswith("listen_addresses") 
    316                     else :     
     316                    else : 
    317317                        tcpip = False 
    318                     if tcpip :     
     318                    if tcpip : 
    319319                        conflines.insert(2, "host\tpykota\tpykotaadmin,pykotauser\t127.0.0.1\t255.255.255.255\tmd5") 
    320320                    else : 
     
    333333                    return (tcpip, port) 
    334334        return (None, None) 
    335          
     335 
    336336    def genConfig(self, adminname, adminemail, dnsdomain, smtpserver, home, tcpip, port) : 
    337337        """Generates minimal configuration files for PyKota.""" 
    338338        if tcpip : 
    339339            storageserver = "localhost:%i" % port 
    340         else :     
     340        else : 
    341341            storageserver = "" 
    342342        conf = pykotaconf % locals() 
     
    356356                    if begin is None : 
    357357                        begin = i 
    358                     else :     
     358                    else : 
    359359                        end = i 
    360                          
     360 
    361361            if (begin is not None) and (end is not None) : 
    362362                suffix = "\n".join(lines[begin+1:end]) 
    363363                self.confirmCommand("to improve PyKota's configuration wrt your existing printers", 'echo "%(suffix)s" >>%(home)s/pykota.conf' % locals()) 
    364          
     364 
    365365    def addPyKotaUser(self) : 
    366366        """Adds a system user named pykota, returns its home directory or None""" 
    367367        try : 
    368368            user = pwd.getpwnam("pykota") 
    369         except KeyError :     
     369        except KeyError : 
    370370            if self.confirmCommand("to create a system user named 'pykota'", self.adduser) : 
    371                 try :     
     371                try : 
    372372                    return pwd.getpwnam("pykota")[5] 
    373                 except KeyError :     
     373                except KeyError : 
    374374                    return None 
    375             else :         
     375            else : 
    376376                return None 
    377         else :     
     377        else : 
    378378            return user[5] 
    379      
     379 
    380380    def setupBackend(self) : 
    381381        """Installs the cupspykota backend.""" 
     
    385385            self.confirmCommand("to make PyKota known to CUPS", "ln -s %(realbackend)s %(backend)s" % locals()) 
    386386            self.confirmCommand("to restart CUPS for the changes to take effect", self.cupsrestart) 
    387          
    388     def managePrinters(self, printers) :     
     387 
     388    def managePrinters(self, printers) : 
    389389        """For each printer, asks if it should be managed with PyKota or not.""" 
    390390        for (queuename, deviceuri) in printers : 
    391391            command = 'pkprinters --add --cups --description "Printer created with pksetup" "%(queuename)s"' % locals() 
    392392            self.confirmCommand("to import the %(queuename)s print queue into PyKota's database and reroute it through PyKota" % locals(), command) 
    393      
     393 
    394394    def installPyKotaFiles(self) : 
    395395        """Installs PyKota files through Python's Distutils mechanism.""" 
     
    399399        if os.path.exists(setuppy) : 
    400400            self.confirmCommand("to install PyKota files on your system", "python %(setuppy)s install" % locals()) 
    401          
     401 
    402402    def setup(self) : 
    403403        """Installation procedure.""" 
     
    411411        if homedirectory is None : 
    412412            logerr("Installation can't proceed. You MUST create a system user named 'pykota'.\n") 
    413         else :     
     413        else : 
    414414            self.upgradeSystem() 
    415415            self.setupPackages() 
     
    422422            print nowready 
    423423            print "The script %s can be used to reinstall in unattended mode.\n" % self.genInstaller() 
    424              
    425     def genInstaller(self) :         
     424 
     425    def genInstaller(self) : 
    426426        """Generates an installer script.""" 
    427427        scriptname = "/tmp/pykota-installer.sh" 
    428         commands = [ "#! /bin/sh",  
     428        commands = [ "#! /bin/sh", 
    429429                     "#", 
    430                      "# PyKota installer script.",  
    431                      "#",  
     430                     "# PyKota installer script.", 
     431                     "#", 
    432432                     "# This script was automatically generated.", 
    433433                     "#", 
    434434                   ] + self.launched 
    435         script = open(scriptname, "w")            
     435        script = open(scriptname, "w") 
    436436        script.write("\n".join(commands)) 
    437437        script.close() 
    438438        os.chmod(scriptname, \ 
    439439                 stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) 
    440         return scriptname     
    441          
    442          
    443 class Debian(PyKotaSetup) :         
     440        return scriptname 
     441 
     442 
     443class Debian(PyKotaSetup) : 
    444444    """Class for Debian installer.""" 
    445     def setupPackages(self) :     
     445    def setupPackages(self) : 
    446446        """Installs missing Debian packages.""" 
    447447        self.confirmCommand("to install missing dependencies", "apt-get install %s" % " ".join(self.packages)) 
    448              
     448 
    449449    def upgradeSystem(self) : 
    450450        """Upgrades the Debian setup.""" 
    451451        if self.confirmCommand("to grab an up-to-date list of available packages", "apt-get update") : 
    452452            self.confirmCommand("to put your system up-to-date", "apt-get -y dist-upgrade") 
    453      
    454 class Ubuntu(Debian) :     
     453 
     454class Ubuntu(Debian) : 
    455455    """Class for Ubuntu installer.""" 
    456456    pass 
    457      
    458 if __name__ == "__main__" :         
     457 
     458if __name__ == "__main__" : 
    459459    retcode = 0 
    460460    if (len(sys.argv) != 2) or (sys.argv[1] == "-h") or (sys.argv[1] == "--help") : 
    461461        print "pksetup v0.1 (c) 2003-2008 Jerome Alet - alet@librelogiciel.com\n\nusage : pksetup distribution\n\ne.g. : pksetup debian\n\nIMPORTANT : only Debian and Ubuntu are currently supported." 
    462     elif (sys.argv[1] == "-v") or (sys.argv[1] == "--version") :     
     462    elif (sys.argv[1] == "-v") or (sys.argv[1] == "--version") : 
    463463        print "0.1" # pksetup's own version number 
    464     else :     
     464    else : 
    465465        classname = sys.argv[1].strip().title() 
    466466        try : 
    467467            installer = globals()[classname]() 
    468         except KeyError :     
     468        except KeyError : 
    469469            logerr("There's currently no support for the %s distribution, sorry.\n" % sys.argv[1]) 
    470470            retcode = -1 
    471         else :     
     471        else : 
    472472            try : 
    473473                retcode = installer.setup() 
    474             except KeyboardInterrupt :              
     474            except KeyboardInterrupt : 
    475475                logerr("\n\n\nWARNING : Setup was aborted at user's request !\n\n") 
    476476                retcode = -1 
  • pykota/trunk/bin/pkturnkey

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    5151  -v | --version       Prints pkturnkey version number then exits. 
    5252  -h | --help          Prints this message then exits. 
    53    
     53 
    5454  -c | --doconf        Give hints about what to put into pykota.conf 
    55    
     55 
    5656  -d | --dousers       Manages users accounts as well. 
    57    
     57 
    5858  -D | --dogroups      Manages users groups as well. 
    5959                       Implies -d | --dousers. 
    60    
     60 
    6161  -e | --emptygroups   Includes empty groups. 
    62    
     62 
    6363  -f | --force         Modifies the database instead of printing what 
    6464                       it would do. 
    65                         
     65 
    6666  -u | --uidmin uid    Only adds users whose uid is greater than or equal to 
    6767                       uid. You can pass an username there as well, and its 
     
    6969                       If not set, 0 will be used automatically. 
    7070                       Implies -d | --dousers. 
    71                         
     71 
    7272  -U | --uidmax uid    Only adds users whose uid is lesser than or equal to 
    7373                       uid. You can pass an username there as well, and its 
     
    8181                       If not set, 0 will be used automatically. 
    8282                       Implies -D | --dogroups. 
    83                         
     83 
    8484  -G | --gidmax gid    Only adds groups whose gid is lesser than or equal to 
    8585                       gid. You can pass a groupname there as well, and its 
     
    8888                       Implies -D | --dogroups. 
    8989 
    90 examples :                               
     90examples : 
    9191 
    9292  $ pkturnkey --dousers --uidmin jerome 
     
    9595  printers and print accounts for all users whose uid is greater than 
    9696  or equal to jerome's one. Won't manage any users group. 
    97    
     97 
    9898  To REALLY initialize the database instead of simulating it, please 
    9999  use the -f | --force command line switch. 
    100    
     100 
    101101  You can limit the initialization to only a subset of the existing 
    102102  printers, by passing their names at the end of the command line. 
    103103""") 
    104          
     104 
    105105class PKTurnKey(Tool) : 
    106106    """A class for an initialization tool.""" 
     
    118118            if self.matchString(queuename, namestomatch) : 
    119119                printers.append((queuename, deviceuri)) 
    120             else :     
     120            else : 
    121121                self.printInfo("Print queue %s skipped." % queuename) 
    122         return printers     
    123          
    124     def listUsers(self, uidmin, uidmax) :     
     122        return printers 
     123 
     124    def listUsers(self, uidmin, uidmax) : 
    125125        """Returns a list of users whose uids are between uidmin and uidmax.""" 
    126126        self.printInfo("Extracting all users whose uid is between %s and %s." % (uidmin, uidmax)) 
    127127        return [(entry[0], entry[3]) for entry in pwd.getpwall() if uidmin <= entry[2] <= uidmax] 
    128          
     128 
    129129    def listGroups(self, gidmin, gidmax, users) : 
    130130        """Returns a list of groups whose gids are between gidmin and gidmax.""" 
     
    135135        for u in users : 
    136136            gidusers.setdefault(u[1], []).append(u[0]) 
    137             usersgid.setdefault(u[0], []).append(u[1])  
    138              
    139         membership = {}     
     137            usersgid.setdefault(u[0], []).append(u[1]) 
     138 
     139        membership = {} 
    140140        for g in range(len(groups)) : 
    141141            (gname, gid, members) = groups[g] 
     
    145145            try : 
    146146                usernames = gidusers[gid] 
    147             except KeyError :     
     147            except KeyError : 
    148148                pass 
    149             else :     
     149            else : 
    150150                for username in usernames : 
    151151                    if not newmembers.has_key(username) : 
    152152                        newmembers[username] = username 
    153             for member in newmembers.keys() :             
     153            for member in newmembers.keys() : 
    154154                if not usersgid.has_key(member) : 
    155155                    del newmembers[member] 
    156156            membership[gname] = newmembers.keys() 
    157157        return membership 
    158          
    159     def runCommand(self, command, dryrun) :     
     158 
     159    def runCommand(self, command, dryrun) : 
    160160        """Launches an external command.""" 
    161161        self.printInfo("%s" % command) 
    162         if not dryrun :     
     162        if not dryrun : 
    163163            os.system(command) 
    164              
    165     def createPrinters(self, printers, dryrun=0) :     
     164 
     165    def createPrinters(self, printers, dryrun=0) : 
    166166        """Creates all printers in PyKota's database.""" 
    167167        if printers : 
     
    171171            args.close() 
    172172            self.runCommand("pkprinters --arguments /tmp/pkprinters.args", dryrun) 
    173          
     173 
    174174    def createUsers(self, users, printers, dryrun=0) : 
    175175        """Creates all users in PyKota's database.""" 
     
    180180            args.close() 
    181181            self.runCommand("pkusers --arguments /tmp/pkusers.users.args", dryrun) 
    182              
     182 
    183183            printersnames = [p[0] for p in printers] 
    184184            args = open("/tmp/edpykota.users.args", "w") 
     
    188188            args.close() 
    189189            self.runCommand("edpykota --arguments /tmp/edpykota.users.args", dryrun) 
    190              
     190 
    191191    def createGroups(self, groups, printers, dryrun=0) : 
    192192        """Creates all groups in PyKota's database.""" 
     
    197197            args.close() 
    198198            self.runCommand("pkusers --arguments /tmp/pkusers.groups.args", dryrun) 
    199              
     199 
    200200            printersnames = [p[0] for p in printers] 
    201201            args = open("/tmp/edpykota.groups.args", "w") 
     
    205205            args.close() 
    206206            self.runCommand("edpykota --arguments /tmp/edpykota.groups.args", dryrun) 
    207              
     207 
    208208            revmembership = {} 
    209209            for (groupname, usernames) in groups.items() : 
    210210                for username in usernames : 
    211211                    revmembership.setdefault(username, []).append(groupname) 
    212             commands = []         
    213             for (username, groupnames) in revmembership.items() :         
     212            commands = [] 
     213            for (username, groupnames) in revmembership.items() : 
    214214                commands.append('pkusers --ingroups %s "%s"' \ 
    215215                    % (",".join(['"%s"' % g for g in groupnames]), username)) 
    216216            for command in commands : 
    217217                self.runCommand(command, dryrun) 
    218          
     218 
    219219    def supportsSNMP(self, hostname, community) : 
    220220        """Returns 1 if the printer accepts SNMP queries, else 0.""" 
     
    222222        try : 
    223223            from pysnmp.entity.rfc3413.oneliner import cmdgen 
    224         except ImportError :     
     224        except ImportError : 
    225225            hasV4 = False 
    226226            try : 
     
    228228                from pysnmp.mapping.udp.role import Manager 
    229229                from pysnmp.proto.api import alpha 
    230             except ImportError :     
     230            except ImportError : 
    231231                logerr("pysnmp doesn't seem to be installed. SNMP checks will be ignored !\n") 
    232232                return 0 
    233         else :         
     233        else : 
    234234            hasV4 = True 
    235              
    236         if hasV4 :     
     235 
     236        if hasV4 : 
    237237            def retrieveSNMPValues(hostname, community) : 
    238238                """Retrieves a printer's internal page counter and status via SNMP.""" 
     
    241241                                                      cmdgen.UdpTransportTarget((hostname, 161)), \ 
    242242                                                      tuple([int(i) for i in pageCounterOID.split('.')])) 
    243                 if errorIndication :                                                   
     243                if errorIndication : 
    244244                    raise "No SNMP !" 
    245                 elif errorStatus :     
     245                elif errorStatus : 
    246246                    raise "No SNMP !" 
    247                 else :                                  
     247                else : 
    248248                    self.SNMPOK = True 
    249249        else : 
    250             def retrieveSNMPValues(hostname, community) :     
     250            def retrieveSNMPValues(hostname, community) : 
    251251                """Retrieves a printer's internal page counter and status via SNMP.""" 
    252252                ver = alpha.protoVersions[alpha.protoVersionId1] 
     
    260260                                       (hostname, 161), \ 
    261261                                       (handleAnswer, req)) 
    262                 except :     
     262                except : 
    263263                    raise "No SNMP !" 
    264264                tsp.close() 
    265              
     265 
    266266            def handleAnswer(wholemsg, notusedhere, req): 
    267267                """Decodes and handles the SNMP answer.""" 
     
    270270                try : 
    271271                    rsp.berDecode(wholemsg) 
    272                 except TypeMismatchError, msg :     
     272                except TypeMismatchError, msg : 
    273273                    raise "No SNMP !" 
    274274                else : 
     
    281281                            for varBind in rsp.apiAlphaGetPdu().apiAlphaGetVarBindList(): 
    282282                                self.values.append(varBind.apiAlphaGetOidVal()[1].rawAsn1Value) 
    283                             try :     
     283                            try : 
    284284                                pagecounter = self.values[0] 
    285285                            except : 
    286286                                raise "No SNMP !" 
    287                             else :     
     287                            else : 
    288288                                self.SNMPOK = 1 
    289289                                return 1 
    290              
     290 
    291291        self.SNMPOK = 0 
    292292        try : 
    293293            retrieveSNMPValues(hostname, community) 
    294         except :     
     294        except : 
    295295            self.SNMPOK = 0 
    296296        return self.SNMPOK 
    297          
     297 
    298298    def supportsPJL(self, hostname, port) : 
    299299        """Returns 1 if the printer accepts PJL queries over TCP, else 0.""" 
    300300        def alarmHandler(signum, frame) : 
    301301            raise "Timeout !" 
    302          
     302 
    303303        pjlsupport = 0 
    304304        signal.signal(signal.SIGALRM, alarmHandler) 
     
    311311            if not answer.startswith("@PJL") : 
    312312                raise "No PJL !" 
    313         except :     
     313        except : 
    314314            pass 
    315         else :     
     315        else : 
    316316            pjlsupport = 1 
    317317        s.close() 
     
    319319        signal.signal(signal.SIGALRM, signal.SIG_IGN) 
    320320        return pjlsupport 
    321              
    322     def hintConfig(self, printers) :     
     321 
     322    def hintConfig(self, printers) : 
    323323        """Gives some hints about what to put into pykota.conf""" 
    324324        if not printers : 
    325325            return 
    326         sys.stderr.flush() # ensure outputs don't mix     
    327         print      
     326        sys.stderr.flush() # ensure outputs don't mix 
     327        print 
    328328        print "--- CUT ---" 
    329329        print "# Here are some lines that we suggest you add at the end" 
     
    338338            try : 
    339339                uri = uri.split("cupspykota:", 2)[-1] 
    340             except (ValueError, IndexError) :     
     340            except (ValueError, IndexError) : 
    341341                pass 
    342             else :     
     342            else : 
    343343                while uri and uri.startswith("/") : 
    344344                    uri = uri[1:] 
    345345                try : 
    346                     (backend, destination) = uri.split(":", 1)  
     346                    (backend, destination) = uri.split(":", 1) 
    347347                    if backend not in ("ipp", "http", "https", "lpd", "socket") : 
    348348                        raise ValueError 
    349                 except ValueError :     
     349                except ValueError : 
    350350                    pass 
    351                 else :         
     351                else : 
    352352                    while destination.startswith("/") : 
    353353                        destination = destination[1:] 
    354                     checkauth = destination.split("@", 1)     
     354                    checkauth = destination.split("@", 1) 
    355355                    if len(checkauth) == 2 : 
    356356                        destination = checkauth[1] 
     
    362362                        except ValueError : 
    363363                            port = 9100 
    364                     else :     
     364                    else : 
    365365                        (hostname, port) = parts[0], 9100 
    366                          
     366 
    367367                    if self.supportsSNMP(hostname, "public") : 
    368368                        accounter = "hardware(snmp)" 
     
    371371                    elif self.supportsPJL(hostname, 9101) : 
    372372                        accounter = "hardware(pjl:9101)" 
    373                     elif self.supportsPJL(hostname, port) :     
     373                    elif self.supportsPJL(hostname, port) : 
    374374                        accounter = "hardware(pjl:%s)" % port 
    375                      
    376             print "preaccounter : software()"  
     375 
     376            print "preaccounter : software()" 
    377377            print "accounter : %s" % accounter 
    378378            print 
    379379        print "--- CUT ---" 
    380          
     380 
    381381    def main(self, names, options) : 
    382382        """Intializes PyKota's database.""" 
    383383        self.adminOnly() 
    384          
     384 
    385385        if not names : 
    386386            names = ["*"] 
    387              
     387 
    388388        self.printInfo(_("Please be patient...")) 
    389389        dryrun = not options["force"] 
    390390        if dryrun : 
    391391            self.printInfo(_("Don't worry, the database WILL NOT BE MODIFIED.")) 
    392         else :     
     392        else : 
    393393            self.printInfo(_("Please WORRY NOW, the database WILL BE MODIFIED.")) 
    394              
    395         if options["dousers"] :     
    396             if not options["uidmin"] :     
     394 
     395        if options["dousers"] : 
     396            if not options["uidmin"] : 
    397397                self.printInfo(_("System users will have a print account as well !"), "warn") 
    398398                uidmin = 0 
    399             else :     
     399            else : 
    400400                try : 
    401401                    uidmin = int(options["uidmin"]) 
    402                 except :     
     402                except : 
    403403                    try : 
    404404                        uidmin = pwd.getpwnam(options["uidmin"])[2] 
    405                     except KeyError, msg :     
     405                    except KeyError, msg : 
    406406                        raise PyKotaCommandLineError, _("Unknown username %s : %s") \ 
    407407                                                   % (options["uidmin"], msg) 
    408                          
    409             if not options["uidmax"] :     
     408 
     409            if not options["uidmax"] : 
    410410                uidmax = sys.maxint 
    411             else :     
     411            else : 
    412412                try : 
    413413                    uidmax = int(options["uidmax"]) 
    414                 except :     
     414                except : 
    415415                    try : 
    416416                        uidmax = pwd.getpwnam(options["uidmax"])[2] 
    417                     except KeyError, msg :     
     417                    except KeyError, msg : 
    418418                        raise PyKotaCommandLineError, _("Unknown username %s : %s") \ 
    419419                                                   % (options["uidmax"], msg) 
    420              
    421             if uidmin > uidmax :             
     420 
     421            if uidmin > uidmax : 
    422422                (uidmin, uidmax) = (uidmax, uidmin) 
    423423            users = self.listUsers(uidmin, uidmax) 
    424         else :     
     424        else : 
    425425            users = [] 
    426              
    427         if options["dogroups"] :     
    428             if not options["gidmin"] :     
     426 
     427        if options["dogroups"] : 
     428            if not options["gidmin"] : 
    429429                self.printInfo(_("System groups will have a print account as well !"), "warn") 
    430430                gidmin = 0 
    431             else :     
     431            else : 
    432432                try : 
    433433                    gidmin = int(options["gidmin"]) 
    434                 except :     
     434                except : 
    435435                    try : 
    436436                        gidmin = grp.getgrnam(options["gidmin"])[2] 
    437                     except KeyError, msg :     
     437                    except KeyError, msg : 
    438438                        raise PyKotaCommandLineError, _("Unknown groupname %s : %s") \ 
    439439                                                   % (options["gidmin"], msg) 
    440                          
    441             if not options["gidmax"] :     
     440 
     441            if not options["gidmax"] : 
    442442                gidmax = sys.maxint 
    443             else :     
     443            else : 
    444444                try : 
    445445                    gidmax = int(options["gidmax"]) 
    446                 except :     
     446                except : 
    447447                    try : 
    448448                        gidmax = grp.getgrnam(options["gidmax"])[2] 
    449                     except KeyError, msg :     
     449                    except KeyError, msg : 
    450450                        raise PyKotaCommandLineError, _("Unknown groupname %s : %s") \ 
    451451                                                   % (options["gidmax"], msg) 
    452              
    453             if gidmin > gidmax :             
     452 
     453            if gidmin > gidmax : 
    454454                (gidmin, gidmax) = (gidmax, gidmin) 
    455455            groups = self.listGroups(gidmin, gidmax, users) 
     
    458458                    if not members : 
    459459                        del groups[groupname] 
    460         else :     
     460        else : 
    461461            groups = [] 
    462              
     462 
    463463        printers = self.listPrinters(names) 
    464464        if printers : 
     
    466466            self.createUsers([entry[0] for entry in users], printers, dryrun) 
    467467            self.createGroups(groups, printers, dryrun) 
    468          
     468 
    469469        if dryrun : 
    470470            self.printInfo(_("Simulation terminated.")) 
    471         else :     
     471        else : 
    472472            self.printInfo(_("Database initialized !")) 
    473         
    474         if options["doconf"] :     
     473 
     474        if options["doconf"] : 
    475475            self.hintConfig(printers) 
    476                      
    477                       
    478 if __name__ == "__main__" :  
     476 
     477 
     478if __name__ == "__main__" : 
    479479    retcode = 0 
    480480    try : 
     
    483483                        "emptygroups", "force", "uidmin=", "uidmax=", \ 
    484484                        "gidmin=", "gidmax=", "doconf"] 
    485          
     485 
    486486        # Initializes the command line tool 
    487487        manager = PKTurnKey(doc=__doc__) 
    488488        manager.deferredInit() 
    489          
     489 
    490490        # parse and checks the command line 
    491491        (options, args) = manager.parseCommandline(sys.argv[1:], \ 
     
    493493                                                   long_options, \ 
    494494                                                   allownothing=1) 
    495          
     495 
    496496        # sets long options 
    497497        options["help"] = options["h"] or options["help"] 
     
    506506        options["gidmax"] = options["G"] or options["gidmax"] 
    507507        options["doconf"] = options["c"] or options["doconf"] 
    508          
     508 
    509509        if options["uidmin"] or options["uidmax"] : 
    510510            if not options["dousers"] : 
    511511                manager.printInfo(_("The --uidmin or --uidmax command line option implies --dousers as well."), "warn") 
    512             options["dousers"] = 1     
    513              
     512            options["dousers"] = 1 
     513 
    514514        if options["gidmin"] or options["gidmax"] : 
    515515            if not options["dogroups"] : 
    516516                manager.printInfo(_("The --gidmin or --gidmax command line option implies --dogroups as well."), "warn") 
    517517            options["dogroups"] = 1 
    518          
     518 
    519519        if options["dogroups"] : 
    520520            if not options["dousers"] : 
    521521                manager.printInfo(_("The --dogroups command line option implies --dousers as well."), "warn") 
    522             options["dousers"] = 1     
    523              
     522            options["dousers"] = 1 
     523 
    524524        if options["help"] : 
    525525            manager.display_usage_and_quit() 
     
    528528        else : 
    529529            retcode = manager.main(args, options) 
    530     except KeyboardInterrupt :         
     530    except KeyboardInterrupt : 
    531531        logerr("\nInterrupted with Ctrl+C !\n") 
    532532        retcode = -3 
    533     except PyKotaCommandLineError, msg :     
     533    except PyKotaCommandLineError, msg : 
    534534        logerr("%s : %s\n" % (sys.argv[0], msg)) 
    535535        retcode = -2 
    536     except SystemExit :         
     536    except SystemExit : 
    537537        pass 
    538538    except : 
    539539        try : 
    540540            manager.crashed("pkturnkey failed") 
    541         except :     
     541        except : 
    542542            crashed("pkturnkey failed") 
    543543        retcode = -1 
     
    545545    try : 
    546546        manager.storage.close() 
    547     except (TypeError, NameError, AttributeError) :     
     547    except (TypeError, NameError, AttributeError) : 
    548548        pass 
    549          
    550     sys.exit(retcode)     
     549 
     550    sys.exit(retcode) 
  • pykota/trunk/bin/pkusers

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    4040 
    4141  pkusers [options] user1 user2 user3 ... userN 
    42    
    43 or :   
     42 
     43or : 
    4444 
    4545  pkusers --groups [options] group1 group2 group3 ... groupN 
     
    4949  -v | --version       Prints pkusers's version number then exits. 
    5050  -h | --help          Prints this message then exits. 
    51    
     51 
    5252  -a | --add           Adds users if they don't exist on the database. 
    5353                       If they exist, they are modified unless 
    5454                       -s|--skipexisting is also used. 
    55                         
     55 
    5656  -d | --delete        Deletes users from the quota storage. 
    5757 
     
    6262 
    6363  -D | --description d Adds a textual description to users or groups. 
    64                         
     64 
    6565  -g | --groups        Edit users groups instead of users. 
    66                            
    67   -o | --overcharge f  Sets the overcharging factor applied to the user  
    68                        when computing the cost of a print job. Positive or  
     66 
     67  -o | --overcharge f  Sets the overcharging factor applied to the user 
     68                       when computing the cost of a print job. Positive or 
    6969                       negative floating point values are allowed, 
    7070                       this allows you to do some really creative 
     
    7474                       cost of the job for a particular user. 
    7575                       Only users have such a coefficient. 
    76    
     76 
    7777  -i | --ingroups g1[,g2...]  Puts the users into each of the groups 
    7878                              listed, separated by commas. The groups 
    7979                              must already exist in the Quota Storage. 
    80                         
     80 
    8181  -L | --list          Lists users or groups. 
    82    
    83   -l | --limitby l     Choose if the user/group is limited in printing                      
     82 
     83  -l | --limitby l     Choose if the user/group is limited in printing 
    8484                       by its account balance or by its page quota. 
    8585                       The default value is 'quota'. Allowed values 
    86                        are 'quota' 'balance' 'noquota' 'noprint'  
     86                       are 'quota' 'balance' 'noquota' 'noprint' 
    8787                       and 'nochange' : 
    88                         
     88 
    8989                         - quota : limit by number of pages per printer. 
    9090                         - balance : limit by number of credits in account. 
    9191                         - noquota : no limit, accounting still done. 
    92                          - nochange : no limit, accounting not done.  
    93                          - noprint : printing is denied.  
     92                         - nochange : no limit, accounting not done. 
     93                         - noprint : printing is denied. 
    9494                       NB : nochange and noprint are not supported for groups. 
    95                         
    96   -b | --balance b     Sets the user's account balance to b.                      
     95 
     96  -b | --balance b     Sets the user's account balance to b. 
    9797                       Account balance may be increase or decreased 
    9898                       if b is prefixed with + or -. 
     
    102102                       Groups don't have a real balance, but the 
    103103                       sum of their users' account balance. 
    104                         
     104 
    105105  -C | --comment txt   Defines some informational text to be associated 
    106106                       with a change to an user's account balance. 
    107107                       Only meaningful if -b | --balance is also used. 
    108                         
    109                         
    110   -r | --remove        In combination with the --ingroups option above,                        
     108 
     109 
     110  -r | --remove        In combination with the --ingroups option above, 
    111111                       remove users from the specified users groups. 
    112                         
     112 
    113113  -s | --skipexisting  In combination with the --add option above, tells 
    114114                       pkusers to not modify existing users. 
    115                         
     115 
    116116  user1 through userN and group1 through groupN can use wildcards 
    117117  if the --add option is not set. 
    118    
    119 examples :                               
     118 
     119examples : 
    120120 
    121121  $ pkusers --add john paul george ringo/ringo@example.com 
    122    
     122 
    123123  This will add users john, paul, george and ringo to the quota 
    124   database. User ringo's email address will also be set to  
     124  database. User ringo's email address will also be set to 
    125125  'ringo@example.com' 
    126    
     126 
    127127  $ pkusers --ingroups coders,it jerome 
    128    
     128 
    129129  User jerome is put into the groups "coders" and "it" which must 
    130130  already exist in the quota database. 
    131              
     131 
    132132  $ pkusers --limitby balance jerome 
    133    
     133 
    134134  This will tell PyKota to limit jerome by his account's balance 
    135135  when printing. 
    136    
     136 
    137137  $ pkusers --balance +10.0 --comment "He paid with his blood !" jerome 
    138    
     138 
    139139  This will increase jerome's account balance by 10.0 (in your 
    140140  own currency). You can decrease the account balance with a 
    141141  dash prefix, and set it to a fixed amount with no prefix. 
    142142  A comment will be stored for this balance change. 
    143    
     143 
    144144  $ pkusers --delete jerome rachel 
    145    
     145 
    146146  This will completely delete jerome and rachel from the quota 
    147147  database. All their quotas and jobs will be deleted too. 
    148    
     148 
    149149  $ pkusers --overcharge 2.5 poorstudent 
    150    
     150 
    151151  This will overcharge the poorstudent user by a factor of 2.5. 
    152    
     152 
    153153  $ pkusers --overcharge -1 jerome 
    154    
     154 
    155155  User jerome will actually earn money whenever he prints. 
    156    
     156 
    157157  $ pkusers --overcharge 0 boss 
    158    
     158 
    159159  User boss can print at will, it won't cost him anything because the 
    160160  cost of each print job will be multiplied by zero before charging 
     
    165165  This will set the email address for each user to username@example.com 
    166166""") 
    167          
    168 class PKUsers(PyKotaTool) :         
     167 
     168class PKUsers(PyKotaTool) : 
    169169    """A class for a users and users groups manager.""" 
    170170    def modifyEntry(self, entry, groups, limitby, description, overcharge=None, balance=None, balancevalue=None, comment=None, email=None) : 
     
    172172        if description is not None : # NB : "" is allowed ! 
    173173            entry.setDescription(description) 
    174         if limitby :     
     174        if limitby : 
    175175            entry.setLimitBy(limitby) 
    176176        if not groups : 
     
    181181                    raise PyKotaCommandLineError, _("Invalid email address %s") % email 
    182182                entry.setEmail(email) 
    183             if overcharge is not None : # NB : 0 is allowed !      
     183            if overcharge is not None : # NB : 0 is allowed ! 
    184184                entry.setOverChargeFactor(overcharge) 
    185185            if balance : 
     
    192192                    newlifetimepaid = float(entry.LifeTimePaid or 0.0) + diff 
    193193                    entry.setAccountBalance(balancevalue, newlifetimepaid, comment) 
    194                      
    195     def manageUsersGroups(self, ugroups, user, remove) :         
     194 
     195    def manageUsersGroups(self, ugroups, user, remove) : 
    196196        """Manage user group membership.""" 
    197197        for ugroup in ugroups : 
     
    200200            else : 
    201201                ugroup.addUserToGroup(user) 
    202                  
     202 
    203203    def main(self, names, options) : 
    204204        """Manage users or groups.""" 
    205205        names = self.sanitizeNames(options, names) 
    206         suffix = (options["groups"] and "Group") or "User"         
    207          
     206        suffix = (options["groups"] and "Group") or "User" 
     207 
    208208        if not options["list"] : 
    209209            percent = Percent(self) 
    210              
     210 
    211211        if not options["add"] : 
    212212            if not options["list"] : 
     
    219219                    percent.display("\n") 
    220220                raise PyKotaCommandLineError, _("There's no %s matching %s") % (_(suffix.lower()), " ".join(names)) 
    221             if not options["list"] :     
     221            if not options["list"] : 
    222222                percent.setSize(len(entries)) 
    223                  
     223 
    224224        if options["list"] : 
    225225            if suffix == "User" : 
     
    229229                    email = entry.Email 
    230230                    if not email : 
    231                         if maildomain :      
     231                        if maildomain : 
    232232                            email = "%s@%s" % (entry.Name, maildomain) 
    233                         elif smtpserver :     
     233                        elif smtpserver : 
    234234                            email = "%s@%s" % (entry.Name, smtpserver) 
    235                         else :     
     235                        else : 
    236236                            email = "%s@%s" % (entry.Name, "localhost") 
    237237                    msg = "%s - <%s>" % (entry.Name, email) 
    238238                    if entry.Description : 
    239239                        msg += " - %s" % entry.Description 
    240                     print msg     
     240                    print msg 
    241241                    print "    %s" % (_("Limited by : %s") % entry.LimitBy) 
    242242                    print "    %s" % (_("Account balance : %.2f") % (entry.AccountBalance or 0.0)) 
     
    244244                    print "    %s" % (_("Overcharging factor : %.2f") % entry.OverCharge) 
    245245                    print 
    246             else :     
     246            else : 
    247247                for entry in entries : 
    248248                    msg = "%s" % entry.Name 
    249249                    if entry.Description : 
    250250                        msg += " - %s" % entry.Description 
    251                     print msg     
     251                    print msg 
    252252                    print "    %s" % (_("Limited by : %s") % entry.LimitBy) 
    253253                    print "    %s" % (_("Group balance : %.2f") % (entry.AccountBalance or 0.0)) 
    254254                    print "    %s" % (_("Total paid so far : %.2f") % (entry.LifeTimePaid or 0.0)) 
    255255                    print 
    256         elif options["delete"] :     
     256        elif options["delete"] : 
    257257            percent.display("\n%s..." % _("Deletion")) 
    258258            getattr(self.storage, "deleteMany%ss" % suffix)(entries) 
     
    266266                                            'noprint', 'nochange') : 
    267267                    raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"] 
    268                 if (limitby in ('nochange', 'noprint')) and options["groups"] :     
     268                if (limitby in ('nochange', 'noprint')) and options["groups"] : 
    269269                    raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"] 
    270                  
     270 
    271271            overcharge = options["overcharge"] 
    272272            if overcharge : 
    273273                try : 
    274274                    overcharge = float(overcharge.strip()) 
    275                 except (ValueError, AttributeError) :     
     275                except (ValueError, AttributeError) : 
    276276                    raise PyKotaCommandLineError, _("Invalid overcharge value %s") % options["overcharge"] 
    277                      
     277 
    278278            balance = options["balance"] 
    279279            if balance : 
     
    281281                try : 
    282282                    balancevalue = float(balance) 
    283                 except ValueError :     
     283                except ValueError : 
    284284                    raise PyKotaCommandLineError, _("Invalid balance value %s") % options["balance"] 
    285             else :     
     285            else : 
    286286                balancevalue = None 
    287                  
     287 
    288288            if options["ingroups"] : 
    289289                usersgroups = self.storage.getMatchingGroups(options["ingroups"]) 
    290290                if not usersgroups : 
    291291                    raise PyKotaCommandLineError, _("There's no users group matching %s") % " ".join(options["ingroups"].split(',')) 
    292             else :          
     292            else : 
    293293                usersgroups = [] 
    294                      
     294 
    295295            description = options["description"] 
    296296            if description : 
    297297                description = description.strip() 
    298                  
     298 
    299299            comment = options["comment"] 
    300300            if comment : 
    301301                comment = comment.strip() 
    302             email = options["email"]     
     302            email = options["email"] 
    303303            if email : 
    304304                email = email.strip() 
    305             skipexisting = options["skipexisting"]     
     305            skipexisting = options["skipexisting"] 
    306306            groups = options["groups"] 
    307307            remove = options["remove"] 
    308308            self.storage.beginTransaction() 
    309             try :     
    310                 if options["add"] :     
    311                     rejectunknown = self.config.getRejectUnknown()     
     309            try : 
     310                if options["add"] : 
     311                    rejectunknown = self.config.getRejectUnknown() 
    312312                    percent.display("%s...\n" % _("Creation")) 
    313313                    percent.setSize(len(names)) 
     
    325325                                    try : 
    326326                                        grp.getgrnam(ename) 
    327                                     except KeyError :     
     327                                    except KeyError : 
    328328                                        self.printInfo(_("Unknown group %s") % ename, "error") 
    329329                                        reject = 1 
    330                                 else :     
     330                                else : 
    331331                                    try : 
    332332                                        pwd.getpwnam(ename) 
    333                                     except KeyError :     
     333                                    except KeyError : 
    334334                                        self.printInfo(_("Unknown user %s") % ename, "error") 
    335335                                        reject = 1 
    336                             if not reject :         
     336                            if not reject : 
    337337                                entry = globals()["Storage%s" % suffix](self.storage, ename) 
    338338                                if groups : 
    339339                                    self.modifyEntry(entry, groups, limitby, \ 
    340340                                                     description) 
    341                                 else :     
     341                                else : 
    342342                                    self.modifyEntry(entry, groups, limitby, \ 
    343343                                                     description, overcharge,\ 
     
    348348                                    if skipexisting : 
    349349                                        self.logdebug(_("%s %s already exists, skipping.") % (_(suffix), ename)) 
    350                                     else :     
     350                                    else : 
    351351                                        self.logdebug(_("%s %s already exists, will be modified.") % (_(suffix), ename)) 
    352352                                        if groups : 
     
    373373                        if groups : 
    374374                            self.modifyEntry(entry, groups, limitby, description) 
    375                         else :     
     375                        else : 
    376376                            self.modifyEntry(entry, groups, limitby, description, \ 
    377377                                             overcharge, balance, balancevalue, \ 
    378378                                             comment, email) 
    379                             self.manageUsersGroups(usersgroups, entry, remove)                 
    380                         entry.save()     
     379                            self.manageUsersGroups(usersgroups, entry, remove) 
     380                        entry.save() 
    381381                        percent.oneMore() 
    382             except :                     
     382            except : 
    383383                self.storage.rollbackTransaction() 
    384384                raise 
    385             else :     
     385            else : 
    386386                self.storage.commitTransaction() 
    387                  
     387 
    388388        if not options["list"] : 
    389389            percent.done() 
    390                       
    391 if __name__ == "__main__" :  
     390 
     391if __name__ == "__main__" : 
    392392    retcode = 0 
    393393    try : 
     
    401401                        "ingroups=", "limitby=", "balance=", "comment=", \ 
    402402                       ] 
    403                          
    404          
     403 
     404 
    405405        # Initializes the command line tool 
    406406        manager = PKUsers(doc=__doc__) 
    407407        manager.deferredInit() 
    408          
     408 
    409409        # parse and checks the command line 
    410410        (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options) 
    411          
     411 
    412412        # sets long options 
    413413        options["help"] = options["h"] or options["help"] 
     
    415415        options["add"] = options["a"] or options["add"] 
    416416        options["description"] = options["D"] or options["description"] 
    417         options["delete"] = options["d"] or options["delete"]  
     417        options["delete"] = options["d"] or options["delete"] 
    418418        options["groups"] = options["g"] or options["groups"] 
    419419        options["list"] = options["L"] or options["list"] 
     
    421421        options["skipexisting"] = options["s"] or options["skipexisting"] 
    422422        options["limitby"] = options["l"] or options["limitby"] 
    423         options["balance"] = options["b"] or options["balance"]  
     423        options["balance"] = options["b"] or options["balance"] 
    424424        options["ingroups"] = options["i"] or options["ingroups"] 
    425425        options["overcharge"] = options["o"] or options["overcharge"] 
    426426        options["comment"] = options["C"] or options["comment"] or defaults["comment"] 
    427427        options["email"] = options["e"] or options["email"] 
    428          
     428 
    429429        if options["help"] : 
    430430            manager.display_usage_and_quit() 
     
    436436           or (options["groups"] and (options["balance"] or options["ingroups"] or options["overcharge"])) : 
    437437            raise PyKotaCommandLineError, _("incompatible options, see help.") 
    438         elif options["remove"] and not options["ingroups"] :     
     438        elif options["remove"] and not options["ingroups"] : 
    439439            raise PyKotaCommandLineError, _("You have to pass user groups names on the command line") 
    440440        elif (not args) and (options["add"] or options["delete"]) : 
     
    442442        else : 
    443443            retcode = manager.main(args, options) 
    444     except KeyboardInterrupt :         
     444    except KeyboardInterrupt : 
    445445        logerr("\nInterrupted with Ctrl+C !\n") 
    446446        retcode = -3 
    447     except PyKotaCommandLineError, msg :     
     447    except PyKotaCommandLineError, msg : 
    448448        logerr("%s : %s\n" % (sys.argv[0], msg)) 
    449449        retcode = -2 
    450     except SystemExit :         
     450    except SystemExit : 
    451451        pass 
    452452    except : 
    453453        try : 
    454454            manager.crashed("pkusers failed") 
    455         except :     
     455        except : 
    456456            crashed("pkusers failed") 
    457457        retcode = -1 
     
    459459    try : 
    460460        manager.storage.close() 
    461     except (TypeError, NameError, AttributeError) :     
     461    except (TypeError, NameError, AttributeError) : 
    462462        pass 
    463          
    464     sys.exit(retcode)     
     463 
     464    sys.exit(retcode) 
  • pykota/trunk/bin/pykosd

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    3131try : 
    3232    import pyosd 
    33 except ImportError :     
     33except ImportError : 
    3434    sys.stderr.write("Sorry ! You need both xosd and the Python OSD library (pyosd) for this software to work.\n") 
    3535    sys.exit(-1) 
     
    5353            if not user.Exists : 
    5454                raise PyKotaCommandLineError, _("You %(username)s don't have a PyKota printing account. Please contact your administrator.") % locals() 
    55             if user.LimitBy == "quota" :     
     55            if user.LimitBy == "quota" : 
    5656                printers = self.storage.getMatchingPrinters("*") 
    5757                upquotas = [ self.storage.getUserPQuotaFromBackend(user, p) for p in printers ] # don't use cache 
    5858                nblines = len(upquotas) 
    59                 display = pyosd.osd(font=options.font,  
    60                                     colour=color,  
    61                                     timeout=options.duration,  
    62                                     shadow=2,  
     59                display = pyosd.osd(font=options.font, 
     60                                    colour=color, 
     61                                    timeout=options.duration, 
     62                                    shadow=2, 
    6363                                    lines=nblines) 
    6464                for line in range(nblines) : 
     
    6767                        if upq.SoftLimit is None : 
    6868                            percent = "%i" % upq.PageCounter 
    69                         else :         
     69                        else : 
    7070                            percent = "%i%%" % min((upq.PageCounter * 100) / upq.SoftLimit, 100) 
    71                     else :         
     71                    else : 
    7272                        percent = "%i%%" % min((upq.PageCounter * 100) / upq.HardLimit, 100) 
    73                     printername = upq.Printer.Name     
     73                    printername = upq.Printer.Name 
    7474                    msg = _("Pages used on %(printername)s : %(percent)s") % locals() 
    75                     display.display(msg.encode(self.charset, "replace"),  
    76                                     type=pyosd.TYPE_STRING,  
     75                    display.display(msg.encode(self.charset, "replace"), 
     76                                    type=pyosd.TYPE_STRING, 
    7777                                    line=line) 
    7878            elif user.LimitBy == "balance" : 
    7979                if user.AccountBalance <= self.config.getBalanceZero() : 
    8080                    color = "#FF0000" 
    81                 display = pyosd.osd(font=options.font,  
    82                                     colour=color,  
    83                                     timeout=options.duration,  
     81                display = pyosd.osd(font=options.font, 
     82                                    colour=color, 
     83                                    timeout=options.duration, 
    8484                                    shadow=2) 
    85                 balance = user.AccountBalance                     
     85                balance = user.AccountBalance 
    8686                msg = _("PyKota Units left : %(balance).2f") % locals() 
    87                 display.display(msg.encode(self.charset, "replace"),  
     87                display.display(msg.encode(self.charset, "replace"), 
    8888                                type=pyosd.TYPE_STRING) 
    89             elif user.LimitBy == "noprint" :     
    90                 display = pyosd.osd(font=options.font,  
    91                                     colour="#FF0000",  
    92                                     timeout=options.duration,  
     89            elif user.LimitBy == "noprint" : 
     90                display = pyosd.osd(font=options.font, 
     91                                    colour="#FF0000", 
     92                                    timeout=options.duration, 
    9393                                    shadow=2) 
    9494                msg = _("Printing denied.") 
    95                 display.display(msg.encode(self.charset, "replace"),  
     95                display.display(msg.encode(self.charset, "replace"), 
    9696                                type=pyosd.TYPE_STRING) 
    97             elif user.LimitBy == "noquota" :     
    98                 display = pyosd.osd(font=options.font,  
    99                                     colour=savecolor,  
    100                                     timeout=options.duration,  
     97            elif user.LimitBy == "noquota" : 
     98                display = pyosd.osd(font=options.font, 
     99                                    colour=savecolor, 
     100                                    timeout=options.duration, 
    101101                                    shadow=2) 
    102102                msg = _("Printing not limited.") 
    103                 display.display(msg.encode(self.charset, "replace"),  
     103                display.display(msg.encode(self.charset, "replace"), 
    104104                                type=pyosd.TYPE_STRING) 
    105             elif user.LimitBy == "nochange" :     
    106                 display = pyosd.osd(font=options.font,  
    107                                     colour=savecolor,  
    108                                     timeout=options.duration,  
     105            elif user.LimitBy == "nochange" : 
     106                display = pyosd.osd(font=options.font, 
     107                                    colour=savecolor, 
     108                                    timeout=options.duration, 
    109109                                    shadow=2) 
    110110                msg = _("Printing not limited, no accounting.") 
    111                 display.display(msg.encode(self.charset, "replace"),  
     111                display.display(msg.encode(self.charset, "replace"), 
    112112                                type=pyosd.TYPE_STRING) 
    113             else :     
     113            else : 
    114114                limitby = repr(user.LimitBy) 
    115115                raise PyKotaToolError, "Incorrect limitation factor %(limitby)s for user %(username)s" % locals() 
    116                  
     116 
    117117            time.sleep(options.duration + 1) 
    118118            if loop : 
     
    120120                if not loop : 
    121121                    break 
    122             time.sleep(options.sleep)         
    123              
    124         return 0     
    125          
     122            time.sleep(options.sleep) 
     123 
     124        return 0 
     125 
    126126if __name__ == "__main__" : 
    127     def checkandset_positiveint(option, opt, value, optionparser) :     
     127    def checkandset_positiveint(option, opt, value, optionparser) : 
    128128        """Checks and sets positive integer values.""" 
    129129        if value < 0 : 
    130130            loginvalidparam(opt, value, option.default) 
    131131            setattr(optionparser.values, option.dest, option.default) 
    132         else :     
     132        else : 
    133133            setattr(optionparser.values, option.dest, value) 
    134              
    135     def checkandset_color(option, opt, value, optionparser) :     
     134 
     135    def checkandset_color(option, opt, value, optionparser) : 
    136136        """Checks and sets the color value.""" 
    137137        if not value.startswith("#") : 
    138138            value = "#%s" % value 
    139         try :     
     139        try : 
    140140            int(value[1:], 16) 
    141         except (ValueError, TypeError) :     
     141        except (ValueError, TypeError) : 
    142142            error = True 
    143         else :     
     143        else : 
    144144            error = False 
    145145        if (len(value) != 7) or error : 
    146146            loginvalidparam(opt, value, option.default) 
    147147            setattr(optionparser.values, option.dest, option.default) 
    148         else :     
     148        else : 
    149149            setattr(optionparser.values, option.dest, value) 
    150              
     150 
    151151    parser = PyKotaOptionParser(description=_("An On Screen Display (OSD) monitor for PyKota's end users.")) 
    152152    parser.add_option("-c", "--color", "--colour", 
     
    182182                            default=180, 
    183183                            help=_("Set the sleeping time in seconds between two refreshes. Defaults to %default seconds.")) 
    184                              
     184 
    185185    parser.add_example('-s 60 --loop 5', 
    186186                       _("This would tell pykosd to display the current user's status for 3 seconds (the default) every 60 seconds, and exit after 5 iterations.")) 
    187                         
     187 
    188188    run(parser, PyKOSD) 
  • pykota/trunk/bin/pykotme

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    3434from pykota.tool import PyKotaTool 
    3535from pykota.accounter import openAccounter 
    36      
    37 class PyKotMe(PyKotaTool) :         
     36 
     37class PyKotMe(PyKotaTool) : 
    3838    """A class for pykotme.""" 
    3939    def main(self, files, options) : 
     
    4646            # TODO : over the second printer below. 
    4747            files.append("-") 
    48              
     48 
    4949        printers = self.storage.getMatchingPrinters(options.printer) 
    5050        if not printers : 
    5151            raise PyKotaCommandLineError, _("There's no printer matching %s") % options.printer 
    52              
     52 
    5353        username = pwd.getpwuid(os.getuid())[0] 
    5454        if options.user : 
    5555            if not self.config.isAdmin : 
    5656                self.printInfo(_("The --user command line option will be ignored because you are not a PyKota Administrator."), "warn") 
    57             else :     
     57            else : 
    5858                username = options.user 
    59              
     59 
    6060        user = self.storage.getUser(username) 
    6161        if not user.Exists : 
    6262            self.printInfo(_("There's no user matching '%(username)s'.") \ 
    6363                                              % locals(), 
    64                            "error")                    
    65         else :                    
     64                           "error") 
     65        else : 
    6666            if user.LimitBy and (user.LimitBy.lower() == "balance"): 
    6767                self.display("%s\n" % (_("Your account balance : %.2f") % (user.AccountBalance or 0.0))) 
    68              
     68 
    6969            sizeprinted = False 
    7070            done = {} 
    7171            for printer in printers : 
    72                 # Now fake some values. TODO : improve API to not need this anymore     
     72                # Now fake some values. TODO : improve API to not need this anymore 
    7373                printername = printer.Name 
    7474                self.PrinterName = printer.Name 
     
    7777                key = self.preaccounter.name + self.preaccounter.arguments 
    7878                if not done.has_key(key) : 
    79                     totalsize = 0     
     79                    totalsize = 0 
    8080                    inkusage = [] 
    81                     for filename in files :     
     81                    for filename in files : 
    8282                        self.DataFile = filename 
    8383                        self.preaccounter.beginJob(None) 
     
    8585                        totalsize += self.preaccounter.getJobSize(None) 
    8686                        inkusage.extend(self.preaccounter.inkUsage) 
    87                     done[key] = (totalsize, inkusage)     
    88                 (totalsize, inkusage) = done[key]     
    89                 if not sizeprinted :     
     87                    done[key] = (totalsize, inkusage) 
     88                (totalsize, inkusage) = done[key] 
     89                if not sizeprinted : 
    9090                    self.display("%s\n" % (_("Job size : %i pages") % totalsize)) 
    9191                    sizeprinted = True 
     
    9494                    if printer.MaxJobSize and (totalsize > printer.MaxJobSize) : 
    9595                        self.display("%s\n" % (_("User %(username)s is not allowed to print so many pages on printer %(printername)s at this time.") % locals())) 
    96                     else :     
     96                    else : 
    9797                        cost = userpquota.computeJobPrice(totalsize, inkusage) 
    9898                        msg = _("Cost on printer %s : %.2f") % (printer.Name, cost) 
    9999                        if printer.PassThrough : 
    100100                            msg = "%s (%s)" % (msg, _("won't be charged, printer is in passthrough mode")) 
    101                         elif user.LimitBy == "nochange" :     
     101                        elif user.LimitBy == "nochange" : 
    102102                            msg = "%s (%s)" % (msg, _("won't be charged, account is immutable")) 
    103103                        self.display("%s\n" % msg) 
    104104            if user.LimitBy == "noprint" : 
    105105                self.display("%s\n" % (_("User %(username)s is forbidden to print at this time.") % locals())) 
    106              
    107 if __name__ == "__main__" :  
     106 
     107if __name__ == "__main__" : 
    108108    parser = PyKotaOptionParser(description=_("Generates print quotes for end users."), 
    109109                                usage="pykotme [options] [files]") 
     
    116116                            help=_("Acts on this user only. Only one username can be specified this way. The default value is the name of the user who launched this command. This option is ignored when the command is not launched by a PyKota Administrator.")) 
    117117 
    118     parser.add_example("--printer apple file1.ps <file2.pclxl",                             
     118    parser.add_example("--printer apple file1.ps <file2.pclxl", 
    119119                       _("This would show the number of pages needed to print these two files, as well as the cost of printing them to the 'apple' printer for the user who launched this command.")) 
    120120    parser.add_example("--user john", 
  • pykota/trunk/bin/README

    r3275 r3413  
    88# the Free Software Foundation, either version 3 of the License, or 
    99# (at your option) any later version. 
    10 #  
     10# 
    1111# This program is distributed in the hope that it will be useful, 
    1212# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1313# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1414# GNU General Public License for more details. 
    15 #  
     15# 
    1616# You should have received a copy of the GNU General Public License 
    1717# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  • pykota/trunk/bin/repykota

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    3636from pykota.tool import PyKotaTool 
    3737from pykota import reporter 
    38          
    39 class RePyKota(PyKotaTool) :         
     38 
     39class RePyKota(PyKotaTool) : 
    4040    """A class for repykota.""" 
    4141    def main(self, ugnames, options) : 
     
    4343        if options.ingroups and options.groups : 
    4444            raise PyKotaCommandLineError, _("Incompatible options, see help.") 
    45              
     45 
    4646        if self.config.isAdmin : 
    4747            # PyKota administrator 
     
    4949                # no username, means all usernames 
    5050                ugnames = [ "*" ] 
    51                  
     51 
    5252            if options.ingroups : 
    5353                groupsnames = options.ingroups.split(",") 
     
    5757                    if not group.Exists : 
    5858                        self.printInfo("Group %s doesn't exist." % group.Name, "warn") 
    59                     else :     
     59                    else : 
    6060                        for user in self.storage.getGroupMembers(group) : 
    6161                            members[user.Name] = user 
    6262                ugnames = [ m for m in members.keys() if self.matchString(m, ugnames) ] 
    63         else :         
     63        else : 
    6464            # reports only the current user 
    6565            if options.ingroups : 
    6666                raise PyKotaCommandLineError, _("Option --ingroups is reserved to PyKota Administrators.") 
    67                  
     67 
    6868            username = pwd.getpwuid(os.geteuid())[0] 
    6969            if options.groups : 
     
    7171                if user.Exists : 
    7272                    ugnames = [ g.Name for g in self.storage.getUserGroups(user) ] 
    73                 else :     
     73                else : 
    7474                    ugnames = [ ] 
    7575            else : 
    7676                ugnames = [ username ] 
    77          
     77 
    7878        printers = self.storage.getMatchingPrinters(options.printer) 
    7979        if not printers : 
    8080            raise PyKotaCommandLineError, _("There's no printer matching %s") % options.printer 
    81              
     81 
    8282        self.reportingtool = reporter.openReporter(self, "text", printers, ugnames, options.groups) 
    8383        print self.reportingtool.generateReport() 
    84                      
    85 if __name__ == "__main__" :  
     84 
     85if __name__ == "__main__" : 
    8686    parser = PyKotaOptionParser(description=_("Minimalist print accounting reports for PyKota. If not launched by a PyKota administrator, additionnal arguments representing users or groups names are ignored, limiting the scope of the reports to the current user."), 
    8787                                usage="repykota [options] [usernames|groupnames]") 
     
    9797                            default="*", 
    9898                            help=_("Acts on this printer only. You can specify several printer names by separating them with commas. The default value is '%default', which means all printers.")) 
    99                              
     99 
    100100    parser.add_example('', 
    101101                       _("This would generate a report for all users on all printers.")) 
     
    104104    parser.add_example('--printer "laser*,*pson" jerome "jo*"', 
    105105                       _("This would generate a report for all users named 'jerome' or whose name begins with 'jo', on all printers which name begins with 'laser' or ends with 'pson'.")) 
    106                         
     106 
    107107    run(parser, RePyKota) 
  • pykota/trunk/bin/waitprinter.sh

    r3389 r3413  
    88# the Free Software Foundation, either version 3 of the License, or 
    99# (at your option) any later version. 
    10 #  
     10# 
    1111# This program is distributed in the hope that it will be useful, 
    1212# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1313# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1414# GNU General Public License for more details. 
    15 #  
     15# 
    1616# You should have received a copy of the GNU General Public License 
    1717# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  • pykota/trunk/bin/warnpykota

    r3411 r3413  
    99# the Free Software Foundation, either version 3 of the License, or 
    1010# (at your option) any later version. 
    11 #  
     11# 
    1212# This program is distributed in the hope that it will be useful, 
    1313# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1414# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1515# GNU General Public License for more details. 
    16 #  
     16# 
    1717# You should have received a copy of the GNU General Public License 
    1818# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     
    2525notifications to users or groups who have reached the limit of their 
    2626printing quota.""" 
    27          
     27 
    2828import sys 
    2929import os 
     
    4141from pykota.tool import PyKotaTool 
    4242 
    43 class WarnPyKota(PyKotaTool) :         
     43class WarnPyKota(PyKotaTool) : 
    4444    """A class for warnpykota.""" 
    4545    def sendMessage(self, adminmail, touser, fullmessage) : 
    4646        """Sends an email message containing headers to some user.""" 
    4747        smtpserver = self.smtpserver 
    48         try :     
     48        try : 
    4949            server = smtplib.SMTP(smtpserver) 
    50         except socket.error, msg :     
     50        except socket.error, msg : 
    5151            self.printInfo(_("Impossible to connect to SMTP server : %(smtpserver)s") \ 
    5252                                                % locals(), \ 
     
    5555            try : 
    5656                server.sendmail(adminmail, [touser], fullmessage) 
    57             except smtplib.SMTPException, answer :     
     57            except smtplib.SMTPException, answer : 
    5858                for (k, v) in answer.recipients.items() : 
    5959                    errormsg = v[0] 
     
    6363                                   "error") 
    6464            server.quit() 
    65              
     65 
    6666    def sendMessageToUser(self, admin, adminmail, user, subject, message) : 
    6767        """Sends an email message to a user.""" 
     
    7676        msg["Date"] = email.Utils.formatdate(localtime=True) 
    7777        self.sendMessage(adminmail, usermail, msg.as_string()) 
    78          
     78 
    7979    def sendMessageToAdmin(self, adminmail, subject, message) : 
    8080        """Sends an email message to the Print Quota administrator.""" 
     
    8686        msg["To"] = adminmail 
    8787        self.sendMessage(adminmail, adminmail, msg.as_string()) 
    88          
     88 
    8989    def warnGroupPQuota(self, grouppquota) : 
    9090        """Checks a group quota and send messages if quota is exceeded on current printer.""" 
     
    9898        if group.LimitBy in ("noquota", "nochange") : 
    9999            action = "ALLOW" 
    100         else :     
     100        else : 
    101101            action = self.checkGroupPQuota(grouppquota) 
    102102            if action.startswith("POLICY_") : 
     
    111111                        if mailto != "EXTERNAL" : 
    112112                            self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), self.config.getHardWarn(printername)) 
    113                         else :     
     113                        else : 
    114114                            self.externalMailTo(arguments, action, user, printer, self.config.getHardWarn(printername)) 
    115             elif action == "WARN" :     
     115            elif action == "WARN" : 
    116116                adminmessage = _("Print Quota low for group %(groupname)s on printer %(printername)s") % locals() 
    117117                self.printInfo(adminmessage) 
    118118                if mailto in [ "BOTH", "ADMIN" ] : 
    119119                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage) 
    120                 if group.LimitBy and (group.LimitBy.lower() == "balance") :  
     120                if group.LimitBy and (group.LimitBy.lower() == "balance") : 
    121121                    message = self.config.getPoorWarn() 
    122                 else :      
     122                else : 
    123123                    message = self.config.getSoftWarn(printername) 
    124124                if mailto in [ "BOTH", "USER", "EXTERNAL" ] : 
     
    126126                        if mailto != "EXTERNAL" : 
    127127                            self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message) 
    128                         else :     
     128                        else : 
    129129                            self.externalMailTo(arguments, action, user, printer, message) 
    130         return action         
    131          
     130        return action 
     131 
    132132    def warnUserPQuota(self, userpquota) : 
    133133        """Checks a user quota and send him a message if quota is exceeded on current printer.""" 
     
    139139        adminmail = self.config.getAdminMail(printername) 
    140140        (mailto, arguments) = self.config.getMailTo(printername) 
    141          
     141 
    142142        if user.LimitBy in ("noquota", "nochange") : 
    143143            action = "ALLOW" 
     
    149149                if mailto != "EXTERNAL" : 
    150150                    self.sendMessageToUser(admin, adminmail, user, _("Printing denied."), message) 
    151                 else :     
     151                else : 
    152152                    self.externalMailTo(arguments, action, user, printer, message) 
    153153            if mailto in [ "BOTH", "ADMIN" ] : 
     
    157157            if action.startswith("POLICY_") : 
    158158                action = action[7:] 
    159                  
     159 
    160160            if action == "DENY" : 
    161161                adminmessage = _("Print Quota exceeded for user %(username)s on printer %(printername)s") % locals() 
     
    165165                    if mailto != "EXTERNAL" : 
    166166                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message) 
    167                     else :     
     167                    else : 
    168168                        self.externalMailTo(arguments, action, user, printer, message) 
    169169                if mailto in [ "BOTH", "ADMIN" ] : 
    170170                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage) 
    171             elif action == "WARN" :     
     171            elif action == "WARN" : 
    172172                adminmessage = _("Print Quota low for user %(username)s on printer %(printername)s") % locals() 
    173173                self.printInfo(adminmessage) 
    174174                if mailto in [ "BOTH", "USER", "EXTERNAL" ] : 
    175                     if user.LimitBy and (user.LimitBy.lower() == "balance") :  
     175                    if user.LimitBy and (user.LimitBy.lower() == "balance") : 
    176176                        message = self.config.getPoorWarn() 
    177                     else :      
     177                    else : 
    178178                        message = self.config.getSoftWarn(printername) 
    179                     if mailto != "EXTERNAL" :     
     179                    if mailto != "EXTERNAL" : 
    180180                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Low"), message) 
    181                     else :     
     181                    else : 
    182182                        self.externalMailTo(arguments, action, user, printer, message) 
    183183                if mailto in [ "BOTH", "ADMIN" ] : 
    184184                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage) 
    185         return action         
    186          
     185        return action 
     186 
    187187    def main(self, ugnames, options) : 
    188188        """Warn users or groups over print quota.""" 
     
    192192                # no username, means all usernames 
    193193                ugnames = [ "*" ] 
    194         else :         
     194        else : 
    195195            # not a PyKota administrator 
    196196            # warns only the current user 
     
    203203                if user.Exists : 
    204204                    ugnames = [ g.Name for g in self.storage.getUserGroups(user) ] 
    205                 else :     
     205                else : 
    206206                    ugnames = [ ] 
    207207            else : 
    208208                ugnames = [ username ] 
    209                  
     209 
    210210        printername = options.printer 
    211211        printers = self.storage.getMatchingPrinters(printername) 
     
    231231                            if not done : 
    232232                                alreadydone[user.Name] = (action in ('WARN', 'DENY')) 
    233                       
    234 if __name__ == "__main__" :  
     233 
     234if __name__ == "__main__" : 
    235235    parser = PyKotaOptionParser(description=_("A tool to warn users and groups who have reached the limit of their printing quota."), 
    236236                                usage="warnpykota [options] [usernames|groupnames]") 
     
    243243                            default="*", 
    244244                            help=_("Acts on this printer only. You can specify several printer names by separating them with commas. The default value is '%default', which means all printers.")) 
    245                              
     245 
    246246    parser.add_example('', 
    247247                       _("This would notify all users who have reached the limit of their printing quota on any printer.")) 
     
    250250    parser.add_example('--groups --printer "HP*,XER*" "dev*"', 
    251251                       _("This would notify all users of the groups whose name begins with 'dev' and for which the printing quota limit is reached on any printer whose name begins with 'HP' or 'XER'.")) 
    252                         
     252 
    253253    run(parser, WarnPyKota)