root / pykota / trunk / bin / pkprinters @ 3418

Revision 3418, 16.0 kB (checked in by jerome, 16 years ago)

pkprinters now supports new style command line handling.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
RevLine 
[1330]1#! /usr/bin/env python
[3411]2# -*- coding: utf-8 -*-*-
[1330]3#
[3260]4# PyKota : Print Quotas for CUPS
[1330]5#
[3275]6# (c) 2003, 2004, 2005, 2006, 2007, 2008 Jerome Alet <alet@librelogiciel.com>
[3260]7# This program is free software: you can redistribute it and/or modify
[1330]8# it under the terms of the GNU General Public License as published by
[3260]9# the Free Software Foundation, either version 3 of the License, or
[1330]10# (at your option) any later version.
[3413]11#
[1330]12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
[3413]16#
[1330]17# You should have received a copy of the GNU General Public License
[3260]18# along with this program.  If not, see <http://www.gnu.org/licenses/>.
[1330]19#
20# $Id$
21#
[2028]22#
[1330]23
[1821]24import os
[1330]25import sys
[1821]26import pwd
[1330]27
[3294]28import pykota.appinit
[3418]29from pykota.utils import run
30from pykota.commandline import PyKotaOptionParser
[3288]31from pykota.errors import PyKotaCommandLineError
[3295]32from pykota.tool import Percent, PyKotaTool
[2768]33from pykota.storage import StoragePrinter
[1330]34
[3051]35from pkipplib import pkipplib
36
[3413]37class PKPrinters(PyKotaTool) :
[2336]38    """A class for a printers manager."""
[2768]39    def modifyPrinter(self, printer, charges, perpage, perjob, description, passthrough, nopassthrough, maxjobsize) :
40        if charges :
[3413]41            printer.setPrices(perpage, perjob)
[2768]42        if description is not None :        # NB : "" is allowed !
43            printer.setDescription(description)
[3413]44        if nopassthrough :
[2768]45            printer.setPassThrough(False)
[3413]46        if passthrough :
[2768]47            printer.setPassThrough(True)
48        if maxjobsize is not None :
49            printer.setMaxJobSize(maxjobsize)
[3413]50
51    def managePrintersGroups(self, pgroups, printer, remove) :
[2768]52        """Manage printer group membership."""
53        for pgroup in pgroups :
54            if remove :
55                pgroup.delPrinterFromGroup(printer)
56            else :
[3413]57                pgroup.addPrinterToGroup(printer)
58
59    def getPrinterDeviceURI(self, printername) :
[3051]60        """Returns the Device URI attribute for a particular printer."""
[3052]61        if not printername :
62            return ""
[3051]63        cups = pkipplib.CUPS()
64        req = cups.newRequest(pkipplib.IPP_GET_PRINTER_ATTRIBUTES)
65        req.operation["printer-uri"] = ("uri", cups.identifierToURI("printers", printername))
66        try :
67            return cups.doRequest(req).printer["device-uri"][0][1]
[3413]68        except :
[3269]69            self.printInfo(_("Impossible to retrieve %(printername)s's DeviceURI") % locals(), "warn")
[3051]70            return ""
[3413]71
[3052]72    def isPrinterCaptured(self, printername=None, deviceuri=None) :
[3051]73        """Returns True if the printer is already redirected through PyKota's backend, else False."""
[3052]74        if (deviceuri or self.getPrinterDeviceURI(printername)).find("cupspykota:") != -1 :
[3051]75            return True
[3413]76        else :
[3051]77            return False
[3413]78
79    def reroutePrinterThroughPyKota(self, printer) :
[3105]80        """Reroutes a CUPS printer through PyKota."""
81        uri = self.getPrinterDeviceURI(printer.Name)
[3269]82        if uri and (not self.isPrinterCaptured(deviceuri=uri)) :
[3105]83             newuri = "cupspykota://%s" % uri
84             os.system('lpadmin -p "%s" -v "%s"' % (printer.Name, newuri))
85             self.logdebug("Printer %s rerouted to %s" % (printer.Name, newuri))
[3413]86
87    def deroutePrinterFromPyKota(self, printer) :
[3105]88        """Deroutes a PyKota printer through CUPS only."""
89        uri = self.getPrinterDeviceURI(printer.Name)
[3269]90        if uri and self.isPrinterCaptured(deviceuri=uri) :
[3105]91             newuri = uri.replace("cupspykota:", "")
92             if newuri.startswith("//") :
93                 newuri = newuri[2:]
94             os.system('lpadmin -p "%s" -v "%s"' % (printer.Name, newuri))
95             self.logdebug("Printer %s rerouted to %s" % (printer.Name, newuri))
[3413]96
[1330]97    def main(self, names, options) :
98        """Manage printers."""
[3418]99        islist = (options.action == "list")
100        isadd = (options.action == "add")
101        isdelete = (options.action == "delete")
102
103        if not islist :
[3367]104            self.adminOnly()
[3413]105
[3418]106        if not names :
107            if isdelete or isadd :
108                raise PyKotaCommandLineError, _("You must specify printers names on the command line.")
109            names = [u"*"]
[3413]110
[3418]111        if not islist :
[2783]112            percent = Percent(self)
[3413]113
[3418]114        if not isadd :
115            if not islist :
[2789]116                percent.display("%s..." % _("Extracting datas"))
[2768]117            printers = self.storage.getMatchingPrinters(",".join(names))
118            if not printers :
[3418]119                if not islist :
[3052]120                    percent.display("\n")
[2768]121                raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(names)
[3418]122            if not islist :
[2783]123                percent.setSize(len(printers))
[3413]124
[3418]125        if islist :
[2768]126            for printer in printers :
127                parents = ", ".join([p.Name for p in self.storage.getParentPrinters(printer)])
[3418]128                self.display("%s [%s] (%s + #*%s)\n" % \
129                                     (printer.Name,
130                                      printer.Description,
131                                      printer.PricePerJob,
132                                      printer.PricePerPage))
133                self.display("    %s\n" % \
134                                     (_("Passthrough mode : %s") % ((printer.PassThrough and _("ON")) or _("OFF"))))
135                self.display("    %s\n" % \
136                                     (_("Maximum job size : %s") % ((printer.MaxJobSize and (_("%s pages") % printer.MaxJobSize)) or _("Unlimited"))))
137                self.display("    %s\n" % (_("Routed through PyKota : %s") % ((self.isPrinterCaptured(printer.Name) and _("YES")) or _("NO"))))
[3413]138                if parents :
[3418]139                    self.display("    %s %s\n" % (_("in"), parents))
140                self.display("\n")
141        elif isdelete :
[2783]142            percent.display("\n%s..." % _("Deletion"))
[2768]143            self.storage.deleteManyPrinters(printers)
[3105]144            percent.display("\n")
[3418]145            if options.cups :
[3105]146                percent.display("%s...\n" % _("Rerouting printers to CUPS"))
[3073]147                for printer in printers :
[3105]148                    self.deroutePrinterFromPyKota(printer)
[3073]149                    percent.oneMore()
[2657]150        else :
[3418]151            if options.groups :
152                printersgroups = self.storage.getMatchingPrinters(options.groups)
[2768]153                if not printersgroups :
[3418]154                    raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(options.groups.split(','))
[3413]155            else :
[2768]156                printersgroups = []
[3413]157
[3418]158            if options.charge :
[2768]159                try :
[3418]160                    charges = [float(part) for part in options.charge.split(',', 1)]
[3413]161                except ValueError :
[3418]162                    raise PyKotaCommandLineError, _("Invalid charge amount value %s") % options.charge
[3413]163                else :
[2768]164                    if len(charges) > 2 :
165                        charges = charges[:2]
166                    if len(charges) != 2 :
167                        charges = [charges[0], None]
168                    (perpage, perjob) = charges
[3413]169            else :
[2768]170                charges = perpage = perjob = None
[3413]171
[3418]172            if options.maxjobsize :
[2768]173                try :
[3418]174                    maxjobsize = int(options.maxjobsize)
[2768]175                    if maxjobsize < 0 :
176                        raise ValueError
[3413]177                except ValueError :
[3418]178                    raise PyKotaCommandLineError, _("Invalid maximum job size value %s") % options.maxjobsize
[3413]179            else :
[2768]180                maxjobsize = None
[3413]181
[3418]182            description = options.description
[2768]183            if description :
184                description = description.strip()
[3413]185
[2770]186            self.storage.beginTransaction()
187            try :
[3418]188                if isadd :
[2783]189                    percent.display("%s...\n" % _("Creation"))
190                    percent.setSize(len(names))
[2782]191                    for pname in names :
[2770]192                        if self.isValidName(pname) :
193                            printer = StoragePrinter(self.storage, pname)
[3418]194                            self.modifyPrinter(printer,
195                                               charges,
196                                               perpage,
197                                               perjob,
198                                               description,
199                                               options.passthrough,
200                                               options.nopassthrough,
201                                               maxjobsize)
[3413]202                            oldprinter = self.storage.addPrinter(printer)
203
[3418]204                            if options.cups :
[3105]205                                 self.reroutePrinterThroughPyKota(printer)
[3413]206
[2770]207                            if oldprinter is not None :
[3418]208                                if options.skipexisting :
[2770]209                                    self.logdebug(_("Printer %s already exists, skipping.") % pname)
[3413]210                                else :
[2770]211                                    self.logdebug(_("Printer %s already exists, will be modified.") % pname)
[3418]212                                    self.modifyPrinter(oldprinter,
213                                                       charges,
214                                                       perpage,
215                                                       perjob,
216                                                       description,
217                                                       options.passthrough,
218                                                       options.nopassthrough,
219                                                       maxjobsize)
[3413]220                                    oldprinter.save()
[3418]221                                    self.managePrintersGroups(printersgroups,
222                                                              oldprinter,
223                                                              options.remove)
224                            else :
[2770]225                                self.managePrintersGroups(printersgroups, \
226                                                          self.storage.getPrinter(pname), \
[3418]227                                                          options.remove)
[3413]228                        else :
[2770]229                            raise PyKotaCommandLineError, _("Invalid printer name %s") % pname
[2782]230                        percent.oneMore()
[3413]231                else :
[2783]232                    percent.display("\n%s...\n" % _("Modification"))
[3413]233                    for printer in printers :
[3418]234                        self.modifyPrinter(printer,
235                                           charges,
236                                           perpage,
237                                           perjob,
238                                           description,
239                                           options.passthrough,
240                                           options.nopassthrough,
241                                           maxjobsize)
[3413]242                        printer.save()
[3418]243                        self.managePrintersGroups(printersgroups,
244                                                  printer,
245                                                  options.remove)
246                        if options.cups :
[3105]247                            self.reroutePrinterThroughPyKota(printer)
[2782]248                        percent.oneMore()
[3413]249            except :
[2770]250                self.storage.rollbackTransaction()
251                raise
[3413]252            else :
[2770]253                self.storage.commitTransaction()
[3413]254
[3418]255        if not islist :
[2782]256            percent.done()
[3413]257
258if __name__ == "__main__" :
[3418]259    parser = PyKotaOptionParser(description=_("Manages PyKota printers."),
260                                usage="pkprinters [options] printer1 printer2 ... printerN")
261    parser.add_option("-a", "--add",
262                            action="store_const",
263                            const="add",
264                            dest="action",
265                            help=_("Add new, or modify existing, printers."))
266    parser.add_option("-c", "--charge",
267                            dest="charge",
268                            help=_("Set the cost per page, and optionally per job, for printing to the specified printers. If both are to be set, separate them with a comma. Floating point and negative values are allowed."))
269    parser.add_option("-C", "--cups",
270                            action="store_true",
271                            dest="cups",
272                            help=_("Tell CUPS to either start or stop managing the specified printers with PyKota."))
273    parser.add_option("-d", "--delete",
274                            action="store_const",
275                            const="delete",
276                            dest="action",
277                            help=_("Delete the specified printers. Also purge the print quota entries and printing history matching the specified printers."))
278    parser.add_option("-D", "--description",
279                            dest="description",
280                            help=_("Set a textual description for the specified printers."))
281    parser.add_option("-g", "--groups",
282                            dest="groups",
283                            help=_("If the --remove option is not used, the default action is to add the specified printers to the specified printers groups. Otherwise they are removed from these groups. The specified printers groups must already exist, and should be created beforehand just like normal printers with this very command."))
284    parser.add_option("-l", "--list",
285                            action="store_const",
286                            const="list",
287                            dest="action",
288                            help=_("Display detailed informations about the specified printers."))
289    parser.add_option("-m", "--maxjobsize",
290                            dest="maxjobsize",
291                            help=_("Set the maximum job size in pages allowed on the specified printers."))
292    parser.add_option("-n", "--nopassthrough",
293                            action="store_true",
294                            dest="nopassthrough",
295                            help=_("Deactivate passthrough mode for the specified printers. This is the normal mode of operations, in which print jobs are accounted for, and are checked against printing quotas and available credits."))
296    parser.add_option("-p", "--passthrough",
297                            action="store_true",
298                            dest="passthrough",
299                            help=_("Activate passthrough mode for the specified printers. In this mode, jobs sent to these printers are not accounted for. This can be useful for exams during which no user should be charged for his printouts."))
300    parser.add_option("-r", "--remove",
301                            action="store_true",
302                            dest="remove",
303                            help=_("When combined with the --groups option, remove printers from the specified printers groups."))
304    parser.add_option("-s", "--skipexisting",
305                            action="store_true",
306                            dest="skipexisting",
307                            help=_("If --add is used, ensure that existing printers won't be modified."))
[3413]308
[3418]309    parser.add_example('--add --cups -D "HP Printer" --charge 0.05,0.1 hp2100 hp2200',
310                       _("Would create three printers named 'hp2100', and 'hp2200' in PyKota's database, while telling CUPS to route all print jobs through PyKota for these printers. Each of them would have 'HP Printer' as its description. Printing to any of them would cost 0.05 credit per page, plus 0.1 credit for each job."))
311    parser.add_example('--delete "*"',
312                       _("Would delete all existing printers and matching print quota entries and printing history from PyKota's database. USE WITH CARE."))
313    parser.add_example('--groups Laser,HP "hp*"',
314                       _("Would add all printers which name begins with 'hp' to the 'Laser' and 'HP' printers groups, which must already exist."))
315    parser.add_example("--groups Lexmark --remove hp2200",
316                       _("Would remove printer 'hp2200' from the 'Lexmark' printers group."))
317    run(parser, PKPrinters)
Note: See TracBrowser for help on using the browser.