Index: /pykota/trunk/bin/pkprinters
===================================================================
--- /pykota/trunk/bin/pkprinters (revision 3413)
+++ /pykota/trunk/bin/pkprinters (revision 3418)
@@ -27,6 +27,6 @@
 
 import pykota.appinit
-from pykota.utils import *
-
+from pykota.utils import run
+from pykota.commandline import PyKotaOptionParser
 from pykota.errors import PyKotaCommandLineError
 from pykota.tool import Percent, PyKotaTool
@@ -34,97 +34,4 @@
 
 from pkipplib import pkipplib
-
-__doc__ = N_("""pkprinters v%(__version__)s (c) %(__years__)s %(__author__)s
-
-A Printers Manager for PyKota.
-
-command line usage :
-
-  pkprinters [options] printer1 printer2 printer3 ... printerN
-
-options :
-
-  -v | --version       Prints pkprinters's version number then exits.
-  -h | --help          Prints this message then exits.
-
-  -a | --add           Adds printers if they don't exist on the Quota
-                       Storage Server. If they exist, they are modified
-                       unless -s|--skipexisting is also used.
-
-  -d | --delete        Deletes printers from the quota storage.
-
-  -D | --description d Adds a textual description to printers.
-
-  -C | --cups          Also modifies the DeviceURI in CUPS' printers.conf
-
-  -c | --charge p[,j]  Sets the price per page and per job to charge.
-                       Job price is optional.
-                       If both are to be set, separate them with a comma.
-                       Floating point and negative values are allowed.
-
-  -g | --groups pg1[,pg2...] Adds or Remove the printer(s) to the printer
-                       groups pg1, pg2, etc... which must already exist.
-                       A printer group is just like a normal printer,
-                       only that it is usually unknown from the printing
-                       system. Create printer groups exactly the same
-                       way that you create printers, then add other
-                       printers to them with this option.
-                       Accounting is done on a printer and on all
-                       the printer groups it belongs to, quota checking
-                       is done on a printer and on all the printer groups
-                       it belongs to.
-                       If the --remove option below is not used, the
-                       default action is to add printers to the specified
-                       printer groups.
-
-  -l | --list          List informations about the printer(s) and the
-                       printers groups it is a member of.
-
-  -r | --remove        In combination with the --groups option above,
-                       remove printers from the specified printers groups.
-
-  -s | --skipexisting  In combination with the --add option above, tells
-                       pkprinters to not modify existing printers.
-
-  -m | --maxjobsize s  Sets the maximum job size allowed on the printer
-                       to s pages.
-
-  -p | --passthrough   Activate passthrough mode for the printer. In this
-                       mode, users are allowed to print without any impact
-                       on their quota or account balance.
-
-  -n | --nopassthrough Deactivate passthrough mode for the printer.
-                       Without -p or -n, printers are created in
-                       normal mode, i.e. no passthrough.
-
-  printer1 through printerN can contain wildcards if the --add option
-  is not set.
-
-examples :
-
-  $ pkprinters --add -D "HP Printer" --charge 0.05,0.1 hp2100 hp2200 hp8000
-
-  Will create three printers named hp2100, hp2200 and hp8000.
-  Their price per page will be set at 0.05 unit, and their price
-  per job will be set at 0.1 unit. Units are in your own currency,
-  or whatever you want them to mean.
-  All of their descriptions will be set to the string "HP Printer".
-  If any of these printers already exists, it will also be modified
-  unless the -s|--skipexisting command line option is also used.
-
-  $ pkprinters --delete "*"
-
-  This will completely delete all printers and associated quota information,
-  as well as their job history. USE WITH CARE !
-
-  $ pkprinters --groups Laser,HP "hp*"
-
-  This will put all printers which name matches "hp*" into printers groups
-  Laser and HP, which MUST already exist.
-
-  $ pkprinters --groups LexMark --remove hp2200
-
-  This will remove the hp2200 printer from the LexMark printer group.
-""")
 
 class PKPrinters(PyKotaTool) :
@@ -190,42 +97,51 @@
     def main(self, names, options) :
         """Manage printers."""
-        if not options["list"] :
+        islist = (options.action == "list")
+        isadd = (options.action == "add")
+        isdelete = (options.action == "delete")
+
+        if not islist :
             self.adminOnly()
 
-        docups = options["cups"]
-
-        if not options["list"] :
+        if not names :
+            if isdelete or isadd :
+                raise PyKotaCommandLineError, _("You must specify printers names on the command line.")
+            names = [u"*"]
+
+        if not islist :
             percent = Percent(self)
 
-        if not options["add"] :
-            if not options["list"] :
+        if not isadd :
+            if not islist :
                 percent.display("%s..." % _("Extracting datas"))
-            if not names :      # NB : can't happen for --delete because it's catched earlier
-                names = ["*"]
             printers = self.storage.getMatchingPrinters(",".join(names))
             if not printers :
-                if not options["list"] :
+                if not islist :
                     percent.display("\n")
                 raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(names)
-            if not options["list"] :
+            if not islist :
                 percent.setSize(len(printers))
 
-        if options["list"] :
+        if islist :
             for printer in printers :
                 parents = ", ".join([p.Name for p in self.storage.getParentPrinters(printer)])
-                print "%s [%s] (%s + #*%s)" % \
-                      (printer.Name, printer.Description, printer.PricePerJob, \
-                       printer.PricePerPage)
-                print "    %s" % (_("Passthrough mode : %s") % ((printer.PassThrough and _("ON")) or _("OFF")))
-                print "    %s" % (_("Maximum job size : %s") % ((printer.MaxJobSize and (_("%s pages") % printer.MaxJobSize)) or _("Unlimited")))
-                print "    %s" % (_("Routed through PyKota : %s") % ((self.isPrinterCaptured(printer.Name) and _("YES")) or _("NO")))
+                self.display("%s [%s] (%s + #*%s)\n" % \
+                                     (printer.Name,
+                                      printer.Description,
+                                      printer.PricePerJob,
+                                      printer.PricePerPage))
+                self.display("    %s\n" % \
+                                     (_("Passthrough mode : %s") % ((printer.PassThrough and _("ON")) or _("OFF"))))
+                self.display("    %s\n" % \
+                                     (_("Maximum job size : %s") % ((printer.MaxJobSize and (_("%s pages") % printer.MaxJobSize)) or _("Unlimited"))))
+                self.display("    %s\n" % (_("Routed through PyKota : %s") % ((self.isPrinterCaptured(printer.Name) and _("YES")) or _("NO"))))
                 if parents :
-                    print "    %s %s" % (_("in"), parents)
-                print
-        elif options["delete"] :
+                    self.display("    %s %s\n" % (_("in"), parents))
+                self.display("\n")
+        elif isdelete :
             percent.display("\n%s..." % _("Deletion"))
             self.storage.deleteManyPrinters(printers)
             percent.display("\n")
-            if docups :
+            if options.cups :
                 percent.display("%s...\n" % _("Rerouting printers to CUPS"))
                 for printer in printers :
@@ -233,16 +149,16 @@
                     percent.oneMore()
         else :
-            if options["groups"] :
-                printersgroups = self.storage.getMatchingPrinters(options["groups"])
+            if options.groups :
+                printersgroups = self.storage.getMatchingPrinters(options.groups)
                 if not printersgroups :
-                    raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(options["groups"].split(','))
+                    raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(options.groups.split(','))
             else :
                 printersgroups = []
 
-            if options["charge"] :
+            if options.charge :
                 try :
-                    charges = [float(part) for part in options["charge"].split(',', 1)]
+                    charges = [float(part) for part in options.charge.split(',', 1)]
                 except ValueError :
-                    raise PyKotaCommandLineError, _("Invalid charge amount value %s") % options["charge"]
+                    raise PyKotaCommandLineError, _("Invalid charge amount value %s") % options.charge
                 else :
                     if len(charges) > 2 :
@@ -254,25 +170,21 @@
                 charges = perpage = perjob = None
 
-            if options["maxjobsize"] :
+            if options.maxjobsize :
                 try :
-                    maxjobsize = int(options["maxjobsize"])
+                    maxjobsize = int(options.maxjobsize)
                     if maxjobsize < 0 :
                         raise ValueError
                 except ValueError :
-                    raise PyKotaCommandLineError, _("Invalid maximum job size value %s") % options["maxjobsize"]
+                    raise PyKotaCommandLineError, _("Invalid maximum job size value %s") % options.maxjobsize
             else :
                 maxjobsize = None
 
-            description = options["description"]
+            description = options.description
             if description :
                 description = description.strip()
 
-            nopassthrough = options["nopassthrough"]
-            passthrough = options["passthrough"]
-            remove = options["remove"]
-            skipexisting = options["skipexisting"]
             self.storage.beginTransaction()
             try :
-                if options["add"] :
+                if isadd :
                     percent.display("%s...\n" % _("Creation"))
                     percent.setSize(len(names))
@@ -280,27 +192,38 @@
                         if self.isValidName(pname) :
                             printer = StoragePrinter(self.storage, pname)
-                            self.modifyPrinter(printer, charges, perpage, perjob, \
-                                           description, passthrough, \
-                                           nopassthrough, maxjobsize)
+                            self.modifyPrinter(printer,
+                                               charges,
+                                               perpage,
+                                               perjob,
+                                               description,
+                                               options.passthrough,
+                                               options.nopassthrough,
+                                               maxjobsize)
                             oldprinter = self.storage.addPrinter(printer)
 
-                            if docups :
+                            if options.cups :
                                  self.reroutePrinterThroughPyKota(printer)
 
                             if oldprinter is not None :
-                                if skipexisting :
+                                if options.skipexisting :
                                     self.logdebug(_("Printer %s already exists, skipping.") % pname)
                                 else :
                                     self.logdebug(_("Printer %s already exists, will be modified.") % pname)
-                                    self.modifyPrinter(oldprinter, charges, \
-                                               perpage, perjob, description, \
-                                               passthrough, nopassthrough, \
-                                               maxjobsize)
+                                    self.modifyPrinter(oldprinter,
+                                                       charges,
+                                                       perpage,
+                                                       perjob,
+                                                       description,
+                                                       options.passthrough,
+                                                       options.nopassthrough,
+                                                       maxjobsize)
                                     oldprinter.save()
-                                    self.managePrintersGroups(printersgroups, oldprinter, remove)
-                            elif printersgroups :
+                                    self.managePrintersGroups(printersgroups,
+                                                              oldprinter,
+                                                              options.remove)
+                            else :
                                 self.managePrintersGroups(printersgroups, \
                                                           self.storage.getPrinter(pname), \
-                                                          remove)
+                                                          options.remove)
                         else :
                             raise PyKotaCommandLineError, _("Invalid printer name %s") % pname
@@ -309,10 +232,17 @@
                     percent.display("\n%s...\n" % _("Modification"))
                     for printer in printers :
-                        self.modifyPrinter(printer, charges, perpage, perjob, \
-                                           description, passthrough, \
-                                           nopassthrough, maxjobsize)
+                        self.modifyPrinter(printer,
+                                           charges,
+                                           perpage,
+                                           perjob,
+                                           description,
+                                           options.passthrough,
+                                           options.nopassthrough,
+                                           maxjobsize)
                         printer.save()
-                        self.managePrintersGroups(printersgroups, printer, remove)
-                        if docups :
+                        self.managePrintersGroups(printersgroups,
+                                                  printer,
+                                                  options.remove)
+                        if options.cups :
                             self.reroutePrinterThroughPyKota(printer)
                         percent.oneMore()
@@ -323,74 +253,66 @@
                 self.storage.commitTransaction()
 
-        if not options["list"] :
+        if not islist :
             percent.done()
 
 if __name__ == "__main__" :
-    retcode = 0
-    try :
-        short_options = "hvaCc:D:dg:lrsnpm:"
-        long_options = ["help", "version", "add", "cups", "charge=", "description=", \
-                        "delete", "groups=", "list", "remove", \
-                        "skipexisting", "passthrough", "nopassthrough", \
-                        "maxjobsize="]
-
-        # Initializes the command line tool
-        manager = PKPrinters(doc=__doc__)
-        manager.deferredInit()
-
-        # parse and checks the command line
-        (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options)
-
-        # sets long options
-        options["help"] = options["h"] or options["help"]
-        options["version"] = options["v"] or options["version"]
-        options["add"] = options["a"] or options["add"]
-        options["cups"] = options["C"] or options["cups"]
-        options["charge"] = options["c"] or options["charge"]
-        options["description"] = options["D"] or options["description"]
-        options["delete"] = options["d"] or options["delete"]
-        options["groups"] = options["g"] or options["groups"]
-        options["list"] = options["l"] or options["list"]
-        options["remove"] = options["r"] or options["remove"]
-        options["skipexisting"] = options["s"] or options["skipexisting"]
-        options["maxjobsize"] = options["m"] or options["maxjobsize"]
-        options["passthrough"] = options["p"] or options["passthrough"]
-        options["nopassthrough"] = options["n"] or options["nopassthrough"]
-
-        if options["help"] :
-            manager.display_usage_and_quit()
-        elif options["version"] :
-            manager.display_version_and_quit()
-        elif (options["delete"] and (options["add"] or options["groups"] or options["charge"] or options["remove"] or options["description"])) \
-           or (options["skipexisting"] and not options["add"]) \
-           or (options["list"] and (options["add"] or options["delete"] or options["groups"] or options["charge"] or options["remove"] or options["description"])) \
-           or (options["passthrough"] and options["nopassthrough"]) \
-           or (options["remove"] and options["add"]) :
-            raise PyKotaCommandLineError, _("incompatible options, see help.")
-        elif options["remove"] and not options["groups"] :
-            raise PyKotaCommandLineError, _("You have to pass printer groups names on the command line")
-        elif (not args) and (options["add"] or options["delete"]) :
-            raise PyKotaCommandLineError, _("You have to pass printer names on the command line")
-        else :
-            retcode = manager.main(args, options)
-    except KeyboardInterrupt :
-        logerr("\nInterrupted with Ctrl+C !\n")
-        retcode = -3
-    except PyKotaCommandLineError, msg :
-        logerr("%s : %s\n" % (sys.argv[0], msg))
-        retcode = -2
-    except SystemExit :
-        pass
-    except :
-        try :
-            manager.crashed("pkprinters failed")
-        except :
-            crashed("pkprinters failed")
-        retcode = -1
-
-    try :
-        manager.storage.close()
-    except (TypeError, NameError, AttributeError) :
-        pass
-
-    sys.exit(retcode)
+    parser = PyKotaOptionParser(description=_("Manages PyKota printers."),
+                                usage="pkprinters [options] printer1 printer2 ... printerN")
+    parser.add_option("-a", "--add",
+                            action="store_const",
+                            const="add",
+                            dest="action",
+                            help=_("Add new, or modify existing, printers."))
+    parser.add_option("-c", "--charge",
+                            dest="charge",
+                            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."))
+    parser.add_option("-C", "--cups",
+                            action="store_true",
+                            dest="cups",
+                            help=_("Tell CUPS to either start or stop managing the specified printers with PyKota."))
+    parser.add_option("-d", "--delete",
+                            action="store_const",
+                            const="delete",
+                            dest="action",
+                            help=_("Delete the specified printers. Also purge the print quota entries and printing history matching the specified printers."))
+    parser.add_option("-D", "--description",
+                            dest="description",
+                            help=_("Set a textual description for the specified printers."))
+    parser.add_option("-g", "--groups",
+                            dest="groups",
+                            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."))
+    parser.add_option("-l", "--list",
+                            action="store_const",
+                            const="list",
+                            dest="action",
+                            help=_("Display detailed informations about the specified printers."))
+    parser.add_option("-m", "--maxjobsize",
+                            dest="maxjobsize",
+                            help=_("Set the maximum job size in pages allowed on the specified printers."))
+    parser.add_option("-n", "--nopassthrough",
+                            action="store_true",
+                            dest="nopassthrough",
+                            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."))
+    parser.add_option("-p", "--passthrough",
+                            action="store_true",
+                            dest="passthrough",
+                            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."))
+    parser.add_option("-r", "--remove",
+                            action="store_true",
+                            dest="remove",
+                            help=_("When combined with the --groups option, remove printers from the specified printers groups."))
+    parser.add_option("-s", "--skipexisting",
+                            action="store_true",
+                            dest="skipexisting",
+                            help=_("If --add is used, ensure that existing printers won't be modified."))
+
+    parser.add_example('--add --cups -D "HP Printer" --charge 0.05,0.1 hp2100 hp2200',
+                       _("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."))
+    parser.add_example('--delete "*"',
+                       _("Would delete all existing printers and matching print quota entries and printing history from PyKota's database. USE WITH CARE."))
+    parser.add_example('--groups Laser,HP "hp*"',
+                       _("Would add all printers which name begins with 'hp' to the 'Laser' and 'HP' printers groups, which must already exist."))
+    parser.add_example("--groups Lexmark --remove hp2200",
+                       _("Would remove printer 'hp2200' from the 'Lexmark' printers group."))
+    run(parser, PKPrinters)
+
