Index: /pykota/trunk/pykota/tool.py
===================================================================
--- /pykota/trunk/pykota/tool.py (revision 2765)
+++ /pykota/trunk/pykota/tool.py (revision 2782)
@@ -78,4 +78,31 @@
     return msg
 
+class Percent :
+    """A class to display progress."""
+    def __init__(self, app, title, size) :
+        """Initializes the engine."""
+        self.app = app
+        self.size = size
+        self.number = 0
+        self.factor = 100.0 / float(size)
+        self.previous = None
+        self.display(title)
+        
+    def display(self, msg) :    
+        """Displays the value."""
+        self.app.display(msg)
+        
+    def oneMore(self) :    
+        """Increments internal counter."""
+        self.number += 1
+        percent = "%.02f" % (float(self.number) * self.factor)
+        if percent != self.previous : # optimize for large number of items
+            self.display("\r%s%%" % percent)
+            self.previous = percent
+            
+    def done(self) :         
+        """Displays the 'done' message."""
+        self.display("\r100.00%%\r        \r%s\n" % _("Done."))
+        
 class Tool :
     """Base class for tools with no database access."""
@@ -201,8 +228,4 @@
             sys.stdout.flush()
             
-    def done(self) :    
-        """Displays the 'done' message."""
-        self.display("\r100.00%%\r        \r%s\n" % _("Done."))
-        
     def logdebug(self, message) :    
         """Logs something to debug output if debug is enabled."""
Index: /pykota/trunk/bin/edpykota
===================================================================
--- /pykota/trunk/bin/edpykota (revision 2772)
+++ /pykota/trunk/bin/edpykota (revision 2782)
@@ -28,5 +28,5 @@
 import os
 
-from pykota.tool import PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
+from pykota.tool import Percent, PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
 from pykota.config import PyKotaConfigError
 from pykota.storage import PyKotaStorageError, StorageUserPQuota, StorageGroupPQuota
@@ -197,9 +197,8 @@
                         print
         elif options["delete"] :    
-            self.display("\n%s..." % _("Deletion"))
+            percent = Percent(self, "\n%s..." % _("Deletion"), len(printers) * len(entries))
             getattr(self.storage, "deleteMany%sPQuotas" % suffix)(printers, entries)
-            self.display("\n")
+            percent.display("\n")
         else :
-            self.display("\n")    
             skipexisting = options["skipexisting"]
             used = options["used"]
@@ -253,5 +252,5 @@
             try :
                 if options["add"] :
-                    self.display("%s...\n" % _("Creation"))
+                    percent = Percent(self, "\n%s...\n" % _("Creation"), len(printers) * len(entries))
                     dicnames = {}
                     for m in entries :
@@ -262,6 +261,4 @@
                             
                     factory = globals()["Storage%sPQuota" % suffix]
-                    nbtotal = len(printers) * len(entries)
-                    i = 0
                     for printer in printers :
                         pname = printer.Name
@@ -285,25 +282,18 @@
                                                         hardreset, suffix, used)
                                     oldpqentry.save()                    
-                            i += 1
-                            percent = 100.0 * float(i) / float(nbtotal)
-                            self.display("\r%.02f%%" % percent)
+                            percent.oneMore()
                 else :        
-                    nbtotal = len(entries) * len(printers)
-                    if nbtotal :
-                        self.display("%s...\n" % _("Modification"))
-                        i = 0
-                        for printer in printers :
-                            for entry in entries :
-                                pqkey = "%s@%s" % (entry.Name, printer.Name)
-                                pqentry = getattr(self.storage, "get%sPQuota" % suffix)(entry, printer)
-                                if pqentry.Exists :     
-                                    self.modifyPQEntry(pqkey, pqentry, noquota, \
-                                                        softlimit, hardlimit, \
-                                                        increase, reset, \
-                                                        hardreset, suffix, used)
-                                    pqentry.save()        
-                                i += 1            
-                                percent = 100.0 * float(i) / float(nbtotal)
-                                self.display("\r%.02f%%" % percent)
+                    percent = Percent(self, "\n%s...\n" % _("Modification"), len(entries) * len(printers))
+                    for printer in printers :
+                        for entry in entries :
+                            pqkey = "%s@%s" % (entry.Name, printer.Name)
+                            pqentry = getattr(self.storage, "get%sPQuota" % suffix)(entry, printer)
+                            if pqentry.Exists :     
+                                self.modifyPQEntry(pqkey, pqentry, noquota, \
+                                                    softlimit, hardlimit, \
+                                                    increase, reset, \
+                                                    hardreset, suffix, used)
+                                pqentry.save()        
+                            percent.oneMore()
             except :                    
                 self.storage.rollbackTransaction()
@@ -312,6 +302,8 @@
                 self.storage.commitTransaction()
                             
-        if not options["list"] :
-            self.done()         
+        try :
+            percent.done()
+        except NameError :    
+            pass
             
 if __name__ == "__main__" : 
Index: /pykota/trunk/bin/pkbcodes
===================================================================
--- /pykota/trunk/bin/pkbcodes (revision 2770)
+++ /pykota/trunk/bin/pkbcodes (revision 2782)
@@ -29,5 +29,5 @@
 import pwd
 
-from pykota.tool import PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
+from pykota.tool import Percent, PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
 from pykota.storage import StorageBillingCode
 
@@ -116,7 +116,7 @@
                        _("credits"))
         elif options["delete"] :    
-            self.display("\n%s..." % _("Deletion"))
+            percent = Percent(self, "\n%s..." % _("Deletion"), len(billingcodes))
             self.storage.deleteManyBillingCodes(billingcodes)
-            self.display("\n")
+            percent.display("\n")
         else :
             reset = options["reset"]
@@ -129,8 +129,6 @@
             try :
                 if options["add"] :    
-                    self.display("%s...\n" % _("Creation"))
-                    nbtotal = len(names)
-                    for i in range(nbtotal) :
-                        bname = names[i]
+                    percent = Percent(self, "%s...\n" % _("Creation"), len(names))
+                    for bname in names :
                         billingcode = StorageBillingCode(self.storage, bname)
                         self.modifyBillingCode(billingcode, reset, description)
@@ -143,15 +141,11 @@
                                 self.modifyBillingCode(oldbillingcode, reset, description)
                                 oldbillingcode.save()
-                        percent = 100.0 * float(i) / float(nbtotal)
-                        self.display("\r%.02f%%" % percent)
+                        percent.oneMore()
                 else :        
-                    self.display("\n%s...\n" % _("Modification"))
-                    nbtotal = len(billingcodes)            
-                    for i in range(nbtotal) :        
-                        billingcode = billingcodes[i]
+                    percent = Percent(self, "\n%s...\n" % _("Modification"), len(billingcodes))
+                    for billingcode in billingcodes :
                         self.modifyBillingCode(billingcode, reset, description)
                         billingcode.save()    
-                        percent = 100.0 * float(i) / float(nbtotal)
-                        self.display("\r%.02f%%" % percent)
+                        percent.oneMore()
             except :                    
                 self.storage.rollbackTransaction()
@@ -160,6 +154,8 @@
                 self.storage.commitTransaction()
                         
-        if not options["list"] :                
-            self.done()
+        try :
+            percent.done()
+        except NameError :    
+            pass
                      
 if __name__ == "__main__" : 
Index: /pykota/trunk/bin/pkusers
===================================================================
--- /pykota/trunk/bin/pkusers (revision 2774)
+++ /pykota/trunk/bin/pkusers (revision 2782)
@@ -30,5 +30,5 @@
 import grp
 
-from pykota.tool import PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
+from pykota.tool import Percent, PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
 from pykota.storage import StorageUser, StorageGroup
 
@@ -235,7 +235,7 @@
                     print
         elif options["delete"] :    
-            self.display("\n%s..." % _("Deletion"))
+            percent = Percent(self, "\n%s..." % _("Deletion"), len(entries))
             getattr(self.storage, "deleteMany%ss" % suffix)(entries)
-            self.display("\n")
+            percent.display("\n")
         else :
             limitby = options["limitby"]
@@ -286,9 +286,7 @@
             try :    
                 if options["add"] :    
-                    self.display("%s...\n" % _("Creation"))
                     rejectunknown = self.config.getRejectUnknown()    
-                    nbtotal = len(names)
-                    for i in range(nbtotal) :
-                        ename = names[i]
+                    percent = Percent(self, "%s...\n" % _("Creation"), len(names))
+                    for ename in names :
                         email = None
                         if not groups :
@@ -347,11 +345,8 @@
                         else :
                             raise PyKotaCommandLineError, _("Invalid name %s") % ename
-                        percent = 100.0 * float(i) / float(nbtotal)
-                        self.display("\r%.02f%%" % percent)
+                        percent.oneMore()
                 else :
-                    self.display("\n%s...\n" % _("Modification"))
-                    nbtotal = len(entries)
-                    for i in range(nbtotal) :        
-                        entry = entries[i]
+                    percent = Percent(self, "\n%s...\n" % _("Modification"), len(entries)) 
+                    for entry in entries :
                         if groups :
                             self.modifyEntry(entry, groups, limitby, description)
@@ -362,6 +357,5 @@
                             self.manageUsersGroups(usersgroups, entry, remove)                
                         entry.save()    
-                        percent = 100.0 * float(i) / float(nbtotal)
-                        self.display("\r%.02f%%" % percent)
+                        percent.oneMore()
             except :                    
                 self.storage.rollbackTransaction()
@@ -369,7 +363,9 @@
             else :    
                 self.storage.commitTransaction()
-                                
-        if not options["list"] :                
-            self.done()
+                
+        try :
+            percent.done()
+        except NameError :    
+            pass
                      
 if __name__ == "__main__" : 
Index: /pykota/trunk/bin/pkprinters
===================================================================
--- /pykota/trunk/bin/pkprinters (revision 2770)
+++ /pykota/trunk/bin/pkprinters (revision 2782)
@@ -29,5 +29,5 @@
 import pwd
 
-from pykota.tool import PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
+from pykota.tool import Percent, PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
 from pykota.storage import StoragePrinter
 
@@ -171,7 +171,7 @@
                 print    
         elif options["delete"] :    
-            self.display("\n%s..." % _("Deletion"))
+            percent = Percent(self, "\n%s..." % _("Deletion"), len(printers))
             self.storage.deleteManyPrinters(printers)
-            self.display("\n")
+            percent.display("\n")
         else :
             if options["groups"] :        
@@ -217,8 +217,6 @@
             try :
                 if options["add"] :    
-                    self.display("%s...\n" % _("Creation"))
-                    nbtotal = len(names)
-                    for i in range(nbtotal) :
-                        pname = names[i]
+                    percent = Percent(self, "%s...\n" % _("Creation"), len(names))
+                    for pname in names :
                         if self.isValidName(pname) :
                             printer = StoragePrinter(self.storage, pname)
@@ -244,11 +242,8 @@
                         else :    
                             raise PyKotaCommandLineError, _("Invalid printer name %s") % pname
-                        percent = 100.0 * float(i) / float(nbtotal)
-                        self.display("\r%.02f%%" % percent)
+                        percent.oneMore()
                 else :        
-                    self.display("\n%s...\n" % _("Modification"))
-                    nbtotal = len(printers)
-                    for i in range(nbtotal) :        
-                        printer = printers[i]
+                    percent = Percent(self, "\n%s...\n" % _("Modification"), len(printers))
+                    for printer in printers :        
                         self.modifyPrinter(printer, charges, perpage, perjob, \
                                            description, passthrough, \
@@ -256,6 +251,5 @@
                         printer.save()    
                         self.managePrintersGroups(printersgroups, printer, remove)
-                        percent = 100.0 * float(i) / float(nbtotal)
-                        self.display("\r%.02f%%" % percent)
+                        percent.oneMore()
             except :                    
                 self.storage.rollbackTransaction()
@@ -263,6 +257,9 @@
             else :    
                 self.storage.commitTransaction()
-        if not options["list"] :                
-            self.done()
+                
+        try :
+            percent.done()
+        except NameError :    
+            pass
                      
 if __name__ == "__main__" : 
