Show
Ignore:
Timestamp:
04/15/10 01:27:45 (14 years ago)
Author:
jerome
Message:

Backport of the fix to #52.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • pykota/branches/1.26_fixes/pykota/storages/sql.py

    r3393 r3522  
    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, write to the Free Software 
     
    2828                           StorageJob, StorageLastJob, StorageUserPQuota, \ 
    2929                           StorageGroupPQuota, StorageBillingCode 
     30 
     31MAXINNAMES = 500 # Maximum number of non-patterns names to use in a single IN statement 
    3032 
    3133class SQLStorage : 
     
    4244        user.Exists = True 
    4345        return user 
    44          
     46 
    4547    def storageGroupFromRecord(self, groupname, record) : 
    4648        """Returns a StorageGroup instance from a database record.""" 
     
    5355        group.Exists = True 
    5456        return group 
    55          
     57 
    5658    def storagePrinterFromRecord(self, printername, record) : 
    5759        """Returns a StoragePrinter instance from a database record.""" 
     
    6971        printer.Exists = True 
    7072        return printer 
    71          
    72     def setJobAttributesFromRecord(self, job, record) :     
     73 
     74    def setJobAttributesFromRecord(self, job, record) : 
    7375        """Sets the attributes of a job from a database record.""" 
    7476        job.ident = record.get("id") 
     
    7880        job.JobPrice = record.get("jobprice") 
    7981        job.JobAction = record.get("action") 
    80         job.JobFileName = self.databaseToUserCharset(record.get("filename") or "")  
    81         job.JobTitle = self.databaseToUserCharset(record.get("title") or "")  
     82        job.JobFileName = self.databaseToUserCharset(record.get("filename") or "") 
     83        job.JobTitle = self.databaseToUserCharset(record.get("title") or "") 
    8284        job.JobCopies = record.get("copies") 
    83         job.JobOptions = self.databaseToUserCharset(record.get("options") or "")  
     85        job.JobOptions = self.databaseToUserCharset(record.get("options") or "") 
    8486        job.JobDate = record.get("jobdate") 
    8587        job.JobHostName = record.get("hostname") 
     
    9597            (job.JobTitle, job.JobFileName, job.JobOptions) = (_("Hidden because of privacy concerns"),) * 3 
    9698        job.Exists = True 
    97          
     99 
    98100    def storageJobFromRecord(self, record) : 
    99101        """Returns a StorageJob instance from a database record.""" 
     
    101103        self.setJobAttributesFromRecord(job, record) 
    102104        return job 
    103          
     105 
    104106    def storageLastJobFromRecord(self, printer, record) : 
    105107        """Returns a StorageLastJob instance from a database record.""" 
     
    107109        self.setJobAttributesFromRecord(lastjob, record) 
    108110        return lastjob 
    109          
     111 
    110112    def storageUserPQuotaFromRecord(self, user, printer, record) : 
    111113        """Returns a StorageUserPQuota instance from a database record.""" 
     
    120122        userpquota.Exists = True 
    121123        return userpquota 
    122          
     124 
    123125    def storageGroupPQuotaFromRecord(self, group, printer, record) : 
    124126        """Returns a StorageGroupPQuota instance from a database record.""" 
     
    135137        grouppquota.Exists = True 
    136138        return grouppquota 
    137          
     139 
    138140    def storageBillingCodeFromRecord(self, billingcode, record) : 
    139141        """Returns a StorageBillingCode instance from a database record.""" 
     
    145147        code.Exists = True 
    146148        return code 
    147          
    148     def createFilter(self, only) :     
     149 
     150    def createFilter(self, only) : 
    149151        """Returns the appropriate SQL filter.""" 
    150152        if only : 
     
    152154            for (k, v) in only.items() : 
    153155                expressions.append("%s=%s" % (k, self.doQuote(self.userCharsetToDatabase(v)))) 
    154             return " AND ".join(expressions)      
    155         return ""         
    156          
    157     def createOrderBy(self, default, ordering) :     
     156            return " AND ".join(expressions) 
     157        return "" 
     158 
     159    def createOrderBy(self, default, ordering) : 
    158160        """Creates a suitable ORDER BY statement based on a list of fieldnames prefixed with '+' (ASC) or '-' (DESC).""" 
    159161        statements = [] 
    160162        if not ordering : 
    161163            ordering = default 
    162         for field in ordering :     
    163             if field.startswith("-") :     
     164        for field in ordering : 
     165            if field.startswith("-") : 
    164166                statements.append("%s DESC" % field[1:]) 
    165167            elif field.startswith("+") : 
    166168                statements.append("%s ASC" % field[1:]) 
    167             else :     
     169            else : 
    168170                statements.append("%s ASC" % field) 
    169         return ", ".join(statements)     
    170          
     171        return ", ".join(statements) 
     172 
    171173    def extractPrinters(self, extractonly={}, ordering=[]) : 
    172174        """Extracts all printer records.""" 
     
    177179        result = self.doRawSearch("SELECT * FROM printers %(thefilter)s ORDER BY %(orderby)s" % locals()) 
    178180        return self.prepareRawResult(result) 
    179          
     181 
    180182    def extractUsers(self, extractonly={}, ordering=[]) : 
    181183        """Extracts all user records.""" 
     
    186188        result = self.doRawSearch("SELECT * FROM users %(thefilter)s ORDER BY %(orderby)s" % locals()) 
    187189        return self.prepareRawResult(result) 
    188          
     190 
    189191    def extractBillingcodes(self, extractonly={}, ordering=[]) : 
    190192        """Extracts all billing codes records.""" 
     
    195197        result = self.doRawSearch("SELECT * FROM billingcodes %(thefilter)s ORDER BY %(orderby)s" % locals()) 
    196198        return self.prepareRawResult(result) 
    197          
     199 
    198200    def extractGroups(self, extractonly={}, ordering=[]) : 
    199201        """Extracts all group records.""" 
     
    204206        result = self.doRawSearch("SELECT groups.*,COALESCE(SUM(balance), 0) AS balance, COALESCE(SUM(lifetimepaid), 0) as lifetimepaid FROM groups LEFT OUTER JOIN users ON users.id IN (SELECT userid FROM groupsmembers WHERE groupid=groups.id) %(thefilter)s GROUP BY groups.id,groups.groupname,groups.limitby,groups.description ORDER BY %(orderby)s" % locals()) 
    205207        return self.prepareRawResult(result) 
    206          
     208 
    207209    def extractPayments(self, extractonly={}, ordering=[]) : 
    208210        """Extracts all payment records.""" 
     
    212214            try : 
    213215                del extractonly[limit] 
    214             except KeyError :     
     216            except KeyError : 
    215217                pass 
    216218        thefilter = self.createFilter(extractonly) 
     
    218220            thefilter = "AND %s" % thefilter 
    219221        (startdate, enddate) = self.cleanDates(startdate, enddate) 
    220         if startdate :  
     222        if startdate : 
    221223            thefilter = "%s AND date>=%s" % (thefilter, self.doQuote(startdate)) 
    222         if enddate :  
     224        if enddate : 
    223225            thefilter = "%s AND date<=%s" % (thefilter, self.doQuote(enddate)) 
    224226        orderby = self.createOrderBy(["+payments.id"], ordering) 
    225227        result = self.doRawSearch("SELECT username,payments.* FROM users,payments WHERE users.id=payments.userid %(thefilter)s ORDER BY %(orderby)s" % locals()) 
    226228        return self.prepareRawResult(result) 
    227          
     229 
    228230    def extractUpquotas(self, extractonly={}, ordering=[]) : 
    229231        """Extracts all userpquota records.""" 
     
    234236        result = self.doRawSearch("SELECT users.username,printers.printername,userpquota.* FROM users,printers,userpquota WHERE users.id=userpquota.userid AND printers.id=userpquota.printerid %(thefilter)s ORDER BY %(orderby)s" % locals()) 
    235237        return self.prepareRawResult(result) 
    236          
     238 
    237239    def extractGpquotas(self, extractonly={}, ordering=[]) : 
    238240        """Extracts all grouppquota records.""" 
     
    243245        result = self.doRawSearch("SELECT groups.groupname,printers.printername,grouppquota.*,coalesce(sum(pagecounter), 0) AS pagecounter,coalesce(sum(lifepagecounter), 0) AS lifepagecounter FROM groups,printers,grouppquota,userpquota WHERE groups.id=grouppquota.groupid AND printers.id=grouppquota.printerid AND userpquota.printerid=grouppquota.printerid AND userpquota.userid IN (SELECT userid FROM groupsmembers WHERE groupsmembers.groupid=grouppquota.groupid) %(thefilter)s GROUP BY grouppquota.id,grouppquota.groupid,grouppquota.printerid,grouppquota.softlimit,grouppquota.hardlimit,grouppquota.datelimit,grouppquota.maxjobsize,groups.groupname,printers.printername ORDER BY %(orderby)s" % locals()) 
    244246        return self.prepareRawResult(result) 
    245          
     247 
    246248    def extractUmembers(self, extractonly={}, ordering=[]) : 
    247249        """Extracts all user groups members.""" 
     
    252254        result = self.doRawSearch("SELECT groups.groupname, users.username, groupsmembers.* FROM groups,users,groupsmembers WHERE users.id=groupsmembers.userid AND groups.id=groupsmembers.groupid %(thefilter)s ORDER BY %(orderby)s" % locals()) 
    253255        return self.prepareRawResult(result) 
    254          
     256 
    255257    def extractPmembers(self, extractonly={}, ordering=[]) : 
    256258        """Extracts all printer groups members.""" 
     
    268270        result = self.doRawSearch("SELECT p1.printername as pgroupname, p2.printername as printername, printergroupsmembers.* FROM printers p1, printers p2, printergroupsmembers WHERE p1.id=printergroupsmembers.groupid AND p2.id=printergroupsmembers.printerid %(thefilter)s ORDER BY %(orderby)s" % locals()) 
    269271        return self.prepareRawResult(result) 
    270          
     272 
    271273    def extractHistory(self, extractonly={}, ordering=[]) : 
    272274        """Extracts all jobhistory records.""" 
     
    276278            try : 
    277279                del extractonly[limit] 
    278             except KeyError :     
     280            except KeyError : 
    279281                pass 
    280282        thefilter = self.createFilter(extractonly) 
     
    282284            thefilter = "AND %s" % thefilter 
    283285        (startdate, enddate) = self.cleanDates(startdate, enddate) 
    284         if startdate :  
     286        if startdate : 
    285287            thefilter = "%s AND jobdate>=%s" % (thefilter, self.doQuote(startdate)) 
    286         if enddate :  
     288        if enddate : 
    287289            thefilter = "%s AND jobdate<=%s" % (thefilter, self.doQuote(enddate)) 
    288290        orderby = self.createOrderBy(["+jobhistory.id"], ordering) 
    289291        result = self.doRawSearch("SELECT users.username,printers.printername,jobhistory.* FROM users,printers,jobhistory WHERE users.id=jobhistory.userid AND printers.id=jobhistory.printerid %(thefilter)s ORDER BY %(orderby)s" % locals()) 
    290292        return self.prepareRawResult(result) 
    291              
     293 
    292294    def filterNames(self, records, attribute, patterns=None) : 
    293295        """Returns a list of 'attribute' from a list of records. 
    294          
     296 
    295297           Logs any missing attribute. 
    296         """    
     298        """ 
    297299        result = [] 
    298300        for record in records : 
     
    307309                    if self.tool.matchString(attrval, patterns) : 
    308310                        result.append(attrval) 
    309                 else :     
     311                else : 
    310312                    result.append(attrval) 
    311         return result    
    312                  
    313     def getAllBillingCodes(self, billingcode=None) :     
     313        return result 
     314 
     315    def getAllBillingCodes(self, billingcode=None) : 
    314316        """Extracts all billing codes or only the billing codes matching the optional parameter.""" 
    315317        result = self.doSearch("SELECT billingcode FROM billingcodes") 
    316318        if result : 
    317319            return self.filterNames(result, "billingcode", billingcode) 
    318         else :     
     320        else : 
    319321            return [] 
    320          
    321     def getAllPrintersNames(self, printername=None) :     
     322 
     323    def getAllPrintersNames(self, printername=None) : 
    322324        """Extracts all printer names or only the printers' names matching the optional parameter.""" 
    323325        result = self.doSearch("SELECT printername FROM printers") 
    324326        if result : 
    325327            return self.filterNames(result, "printername", printername) 
    326         else :     
     328        else : 
    327329            return [] 
    328      
    329     def getAllUsersNames(self, username=None) :     
     330 
     331    def getAllUsersNames(self, username=None) : 
    330332        """Extracts all user names.""" 
    331333        result = self.doSearch("SELECT username FROM users") 
    332334        if result : 
    333335            return self.filterNames(result, "username", username) 
    334         else :     
     336        else : 
    335337            return [] 
    336          
    337     def getAllGroupsNames(self, groupname=None) :     
     338 
     339    def getAllGroupsNames(self, groupname=None) : 
    338340        """Extracts all group names.""" 
    339341        result = self.doSearch("SELECT groupname FROM groups") 
     
    342344        else : 
    343345            return [] 
    344          
     346 
    345347    def getUserNbJobsFromHistory(self, user) : 
    346348        """Returns the number of jobs the user has in history.""" 
     
    349351            return result[0]["count"] 
    350352        return 0 
    351          
    352     def getUserFromBackend(self, username) :     
     353 
     354    def getUserFromBackend(self, username) : 
    353355        """Extracts user information given its name.""" 
    354356        result = self.doSearch("SELECT * FROM users WHERE username=%s LIMIT 1"\ 
     
    356358        if result : 
    357359            return self.storageUserFromRecord(username, result[0]) 
    358         else :     
     360        else : 
    359361            return StorageUser(self, username) 
    360         
    361     def getGroupFromBackend(self, groupname) :     
     362 
     363    def getGroupFromBackend(self, groupname) : 
    362364        """Extracts group information given its name.""" 
    363365        result = self.doSearch("SELECT groups.*,COALESCE(SUM(balance), 0.0) AS balance, COALESCE(SUM(lifetimepaid), 0.0) AS lifetimepaid FROM groups LEFT OUTER JOIN users ON users.id IN (SELECT userid FROM groupsmembers WHERE groupid=groups.id) WHERE groupname=%s GROUP BY groups.id,groups.groupname,groups.limitby,groups.description LIMIT 1" \ 
     
    365367        if result : 
    366368            return self.storageGroupFromRecord(groupname, result[0]) 
    367         else :     
     369        else : 
    368370            return StorageGroup(self, groupname) 
    369         
    370     def getPrinterFromBackend(self, printername) :         
     371 
     372    def getPrinterFromBackend(self, printername) : 
    371373        """Extracts printer information given its name.""" 
    372374        result = self.doSearch("SELECT * FROM printers WHERE printername=%s LIMIT 1" \ 
     
    374376        if result : 
    375377            return self.storagePrinterFromRecord(printername, result[0]) 
    376         else :     
     378        else : 
    377379            return StoragePrinter(self, printername) 
    378          
    379     def getBillingCodeFromBackend(self, label) :         
     380 
     381    def getBillingCodeFromBackend(self, label) : 
    380382        """Extracts a billing code information given its name.""" 
    381383        result = self.doSearch("SELECT * FROM billingcodes WHERE billingcode=%s LIMIT 1" \ 
     
    383385        if result : 
    384386            return self.storageBillingCodeFromRecord(label, result[0]) 
    385         else :     
     387        else : 
    386388            return StorageBillingCode(self, label) 
    387          
    388     def getUserPQuotaFromBackend(self, user, printer) :         
     389 
     390    def getUserPQuotaFromBackend(self, user, printer) : 
    389391        """Extracts a user print quota.""" 
    390392        if printer.Exists and user.Exists : 
     
    394396                return self.storageUserPQuotaFromRecord(user, printer, result[0]) 
    395397        return StorageUserPQuota(self, user, printer) 
    396          
    397     def getGroupPQuotaFromBackend(self, group, printer) :         
     398 
     399    def getGroupPQuotaFromBackend(self, group, printer) : 
    398400        """Extracts a group print quota.""" 
    399401        if printer.Exists and group.Exists : 
     
    403405                return self.storageGroupPQuotaFromRecord(group, printer, result[0]) 
    404406        return StorageGroupPQuota(self, group, printer) 
    405          
    406     def getPrinterLastJobFromBackend(self, printer) :         
     407 
     408    def getPrinterLastJobFromBackend(self, printer) : 
    407409        """Extracts a printer's last job information.""" 
    408410        result = self.doSearch("SELECT jobhistory.id, jobid, userid, username, pagecounter, jobsize, jobprice, filename, title, copies, options, hostname, jobdate, md5sum, pages, billingcode, precomputedjobsize, precomputedjobprice FROM jobhistory, users WHERE printerid=%s AND userid=users.id ORDER BY jobdate DESC LIMIT 1" % self.doQuote(printer.ident)) 
    409411        if result : 
    410412            return self.storageLastJobFromRecord(printer, result[0]) 
    411         else :     
     413        else : 
    412414            return StorageLastJob(self, printer) 
    413              
    414     def getGroupMembersFromBackend(self, group) :         
     415 
     416    def getGroupMembersFromBackend(self, group) : 
    415417        """Returns the group's members list.""" 
    416418        groupmembers = [] 
     
    422424                groupmembers.append(user) 
    423425                self.cacheEntry("USERS", user.Name, user) 
    424         return groupmembers         
    425          
    426     def getUserGroupsFromBackend(self, user) :         
     426        return groupmembers 
     427 
     428    def getUserGroupsFromBackend(self, user) : 
    427429        """Returns the user's groups list.""" 
    428430        groups = [] 
     
    431433            for record in result : 
    432434                groups.append(self.getGroup(self.databaseToUserCharset(record.get("groupname")))) 
    433         return groups         
    434          
    435     def getParentPrintersFromBackend(self, printer) :     
     435        return groups 
     436 
     437    def getParentPrintersFromBackend(self, printer) : 
    436438        """Get all the printer groups this printer is a member of.""" 
    437439        pgroups = [] 
     
    444446                        pgroups.append(parentprinter) 
    445447        return pgroups 
    446          
     448 
     449    def hasWildCards(self, pattern) : 
     450        """Returns True if the pattern contains wildcards, else False.""" 
     451        specialchars = "*?[!" # no need to check for ] since [ would be there first 
     452        for specialchar in specialchars : 
     453            if specialchar in pattern : 
     454                return True 
     455        return False 
     456 
    447457    def getMatchingPrinters(self, printerpattern) : 
    448458        """Returns the list of all printers for which name matches a certain pattern.""" 
     
    454464        if result : 
    455465            patterns = printerpattern.split(",") 
    456             try : 
    457                 patdict = {}.fromkeys(patterns) 
    458             except AttributeError :     
    459                 # Python v2.2 or earlier 
    460                 patdict = {} 
    461                 for p in patterns : 
    462                     patdict[p] = None 
     466            patdict = {}.fromkeys(patterns) 
    463467            for record in result : 
    464468                pname = self.databaseToUserCharset(record["printername"]) 
     
    467471                    printers.append(printer) 
    468472                    self.cacheEntry("PRINTERS", printer.Name, printer) 
    469         return printers         
    470          
     473        return printers 
     474 
    471475    def getMatchingUsers(self, userpattern) : 
    472476        """Returns the list of all users for which name matches a certain pattern.""" 
     
    475479        # but we don't because other storages semantics may be different, so every 
    476480        # storage should use fnmatch to match patterns and be storage agnostic 
    477         result = self.doSearch("SELECT * FROM users") 
    478         if result : 
    479             patterns = userpattern.split(",") 
    480             try : 
    481                 patdict = {}.fromkeys(patterns) 
    482             except AttributeError :     
    483                 # Python v2.2 or earlier 
    484                 patdict = {} 
    485                 for p in patterns : 
    486                     patdict[p] = None 
    487             for record in result : 
    488                 uname = self.databaseToUserCharset(record["username"]) 
    489                 if patdict.has_key(uname) or self.tool.matchString(uname, patterns) : 
    490                     user = self.storageUserFromRecord(uname, record) 
    491                     users.append(user) 
    492                     self.cacheEntry("USERS", user.Name, user) 
    493         return users         
    494          
     481        # 
     482        # This doesn't prevent us from being smarter, thanks to bse@chalmers.se 
     483        userpattern = userpattern or "*" 
     484        patterns = userpattern.split(",") 
     485        patdict = {}.fromkeys(patterns) 
     486        patterns = patdict.keys() # Ensures the uniqueness of each pattern, but we lose the cmd line ordering 
     487        # BEWARE : if a single pattern contains wild cards, we'll still use the slow route. 
     488        if self.hasWildCards(userpattern) : 
     489            # Slow route 
     490            result = self.doSearch("SELECT * FROM users") 
     491            if result : 
     492                for record in result : 
     493                    uname = self.databaseToUserCharset(record["username"]) 
     494                    if patdict.has_key(uname) or self.tool.matchString(uname, patterns) : 
     495                        user = self.storageUserFromRecord(uname, record) 
     496                        users.append(user) 
     497                        self.cacheEntry("USERS", user.Name, user) 
     498        else : 
     499            # Fast route (probably not faster with a few users) 
     500            while patterns : 
     501                subset = patterns[:MAXINNAMES] 
     502                nbpatterns = len(subset) 
     503                if nbpatterns == 1 : 
     504                    wherestmt = "username=%s" % self.doQuote(self.userCharsetToDatabase(subset[0])) 
     505                else : 
     506                    wherestmt = "username IN (%s)" % ",".join([self.doQuote(self.userCharsetToDatabase(p)) for p in subset]) 
     507                result = self.doSearch("SELECT * FROM users WHERE %s" % wherestmt) 
     508                if result : 
     509                    for record in result : 
     510                        uname = self.databaseToUserCharset(record["username"]) 
     511                        user = self.storageUserFromRecord(uname, record) 
     512                        users.append(user) 
     513                        self.cacheEntry("USERS", user.Name, user) 
     514                patterns = patterns[MAXINNAMES:] 
     515        users.sort(key=lambda u : u.Name) # Adds some ordering, we've already lost the cmd line one anyway. 
     516        return users 
     517 
    495518    def getMatchingGroups(self, grouppattern) : 
    496519        """Returns the list of all groups for which name matches a certain pattern.""" 
     
    502525        if result : 
    503526            patterns = grouppattern.split(",") 
    504             try : 
    505                 patdict = {}.fromkeys(patterns) 
    506             except AttributeError :     
    507                 # Python v2.2 or earlier 
    508                 patdict = {} 
    509                 for p in patterns : 
    510                     patdict[p] = None 
     527            patdict = {}.fromkeys(patterns) 
    511528            for record in result : 
    512529                gname = self.databaseToUserCharset(record["groupname"]) 
     
    515532                    groups.append(group) 
    516533                    self.cacheEntry("GROUPS", group.Name, group) 
    517         return groups         
    518          
     534        return groups 
     535 
    519536    def getMatchingBillingCodes(self, billingcodepattern) : 
    520537        """Returns the list of all billing codes for which the label matches a certain pattern.""" 
     
    523540        if result : 
    524541            patterns = billingcodepattern.split(",") 
    525             try : 
    526                 patdict = {}.fromkeys(patterns) 
    527             except AttributeError :     
    528                 # Python v2.2 or earlier 
    529                 patdict = {} 
    530                 for p in patterns : 
    531                     patdict[p] = None 
     542            patdict = {}.fromkeys(patterns) 
    532543            for record in result : 
    533544                codename = self.databaseToUserCharset(record["billingcode"]) 
     
    536547                    codes.append(code) 
    537548                    self.cacheEntry("BILLINGCODES", code.BillingCode, code) 
    538         return codes         
    539          
    540     def getPrinterUsersAndQuotas(self, printer, names=["*"]) :         
     549        return codes 
     550 
     551    def getPrinterUsersAndQuotas(self, printer, names=["*"]) : 
    541552        """Returns the list of users who uses a given printer, along with their quotas.""" 
    542553        usersandquotas = [] 
     
    552563                    self.cacheEntry("USERPQUOTAS", "%s@%s" % (user.Name, printer.Name), userpquota) 
    553564        return usersandquotas 
    554                  
    555     def getPrinterGroupsAndQuotas(self, printer, names=["*"]) :         
     565 
     566    def getPrinterGroupsAndQuotas(self, printer, names=["*"]) : 
    556567        """Returns the list of groups which uses a given printer, along with their quotas.""" 
    557568        groupsandquotas = [] 
     
    565576                    groupsandquotas.append((group, grouppquota)) 
    566577        return groupsandquotas 
    567          
    568     def addPrinter(self, printer) :         
     578 
     579    def addPrinter(self, printer) : 
    569580        """Adds a printer to the quota storage, returns the old value if it already exists.""" 
    570581        oldentry = self.getPrinter(printer.Name) 
     
    580591        printer.isDirty = False 
    581592        return None # the entry created doesn't need further modification 
    582          
     593 
    583594    def addBillingCode(self, bcode) : 
    584595        """Adds a billing code to the quota storage, returns the old value if it already exists.""" 
     
    587598            return oldentry 
    588599        self.doModify("INSERT INTO billingcodes (billingcode, balance, pagecounter, description) VALUES (%s, %s, %s, %s)" \ 
    589                            % (self.doQuote(self.userCharsetToDatabase(bcode.BillingCode)),  
     600                           % (self.doQuote(self.userCharsetToDatabase(bcode.BillingCode)), 
    590601                              self.doQuote(bcode.Balance or 0.0), \ 
    591602                              self.doQuote(bcode.PageCounter or 0), \ 
     
    593604        bcode.isDirty = False 
    594605        return None # the entry created doesn't need further modification 
    595          
    596     def addUser(self, user) :         
     606 
     607    def addUser(self, user) : 
    597608        """Adds a user to the quota storage, returns the old value if it already exists.""" 
    598609        oldentry = self.getUser(user.Name) 
     
    613624        user.isDirty = False 
    614625        return None # the entry created doesn't need further modification 
    615          
    616     def addGroup(self, group) :         
     626 
     627    def addGroup(self, group) : 
    617628        """Adds a group to the quota storage, returns the old value if it already exists.""" 
    618629        oldentry = self.getGroup(group.Name) 
     
    626637        return None # the entry created doesn't need further modification 
    627638 
    628     def addUserToGroup(self, user, group) :     
     639    def addUserToGroup(self, user, group) : 
    629640        """Adds an user to a group.""" 
    630641        result = self.doSearch("SELECT COUNT(*) AS mexists FROM groupsmembers WHERE groupid=%s AND userid=%s" % (self.doQuote(group.ident), self.doQuote(user.ident))) 
    631642        try : 
    632643            mexists = int(result[0].get("mexists")) 
    633         except (IndexError, TypeError) :     
     644        except (IndexError, TypeError) : 
    634645            mexists = 0 
    635         if not mexists :     
     646        if not mexists : 
    636647            self.doModify("INSERT INTO groupsmembers (groupid, userid) VALUES (%s, %s)" % (self.doQuote(group.ident), self.doQuote(user.ident))) 
    637              
    638     def delUserFromGroup(self, user, group) :     
     648 
     649    def delUserFromGroup(self, user, group) : 
    639650        """Removes an user from a group.""" 
    640651        self.doModify("DELETE FROM groupsmembers WHERE groupid=%s AND userid=%s" % \ 
    641652                       (self.doQuote(group.ident), self.doQuote(user.ident))) 
    642              
     653 
    643654    def addUserPQuota(self, upq) : 
    644655        """Initializes a user print quota on a printer.""" 
     
    658669        upq.isDirty = False 
    659670        return None # the entry created doesn't need further modification 
    660          
     671 
    661672    def addGroupPQuota(self, gpq) : 
    662673        """Initializes a group print quota on a printer.""" 
     
    673684        gpq.isDirty = False 
    674685        return None # the entry created doesn't need further modification 
    675          
    676     def savePrinter(self, printer) :     
     686 
     687    def savePrinter(self, printer) : 
    677688        """Saves the printer to the database in a single operation.""" 
    678689        self.doModify("UPDATE printers SET passthrough=%s, maxjobsize=%s, description=%s, priceperpage=%s, priceperjob=%s WHERE id=%s" \ 
     
    683694                                 self.doQuote(printer.PricePerJob or 0.0), \ 
    684695                                 self.doQuote(printer.ident))) 
    685                                   
    686     def saveUser(self, user) :         
     696 
     697    def saveUser(self, user) : 
    687698        """Saves the user to the database in a single operation.""" 
    688699        self.doModify("UPDATE users SET limitby=%s, balance=%s, lifetimepaid=%s, email=%s, overcharge=%s, description=%s WHERE id=%s" \ 
     
    694705                                  self.doQuote(self.userCharsetToDatabase(user.Description)), \ 
    695706                                  self.doQuote(user.ident))) 
    696                                    
    697     def saveGroup(self, group) :         
     707 
     708    def saveGroup(self, group) : 
    698709        """Saves the group to the database in a single operation.""" 
    699710        self.doModify("UPDATE groups SET limitby=%s, description=%s WHERE id=%s" \ 
     
    701712                                  self.doQuote(self.userCharsetToDatabase(group.Description)), \ 
    702713                                  self.doQuote(group.ident))) 
    703          
    704     def writeUserPQuotaDateLimit(self, userpquota, datelimit) :     
     714 
     715    def writeUserPQuotaDateLimit(self, userpquota, datelimit) : 
    705716        """Sets the date limit permanently for a user print quota.""" 
    706717        self.doModify("UPDATE userpquota SET datelimit=%s WHERE id=%s" % (self.doQuote(datelimit), self.doQuote(userpquota.ident))) 
    707              
    708     def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) :     
     718 
     719    def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) : 
    709720        """Sets the date limit permanently for a group print quota.""" 
    710721        self.doModify("UPDATE grouppquota SET datelimit=%s WHERE id=%s" % (self.doQuote(datelimit), self.doQuote(grouppquota.ident))) 
    711          
    712     def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) :     
     722 
     723    def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) : 
    713724        """Increase page counters for a user print quota.""" 
    714725        self.doModify("UPDATE userpquota SET pagecounter=pagecounter + %s,lifepagecounter=lifepagecounter + %s WHERE id=%s" % (self.doQuote(nbpages), self.doQuote(nbpages), self.doQuote(userpquota.ident))) 
    715         
    716     def saveBillingCode(self, bcode) :     
     726 
     727    def saveBillingCode(self, bcode) : 
    717728        """Saves the billing code to the database.""" 
    718729        self.doModify("UPDATE billingcodes SET balance=%s, pagecounter=%s, description=%s WHERE id=%s" \ 
     
    721732                               self.doQuote(self.userCharsetToDatabase(bcode.Description)), \ 
    722733                               self.doQuote(bcode.ident))) 
    723         
     734 
    724735    def consumeBillingCode(self, bcode, pagecounter, balance) : 
    725736        """Consumes from a billing code.""" 
    726737        self.doModify("UPDATE billingcodes SET balance=balance + %s, pagecounter=pagecounter + %s WHERE id=%s" % (self.doQuote(balance), self.doQuote(pagecounter), self.doQuote(bcode.ident))) 
    727         
    728     def refundJob(self, jobident) :    
     738 
     739    def refundJob(self, jobident) : 
    729740        """Marks a job as refunded in the history.""" 
    730741        self.doModify("UPDATE jobhistory SET action='REFUND' WHERE id=%s;" % self.doQuote(jobident)) 
    731          
    732     def decreaseUserAccountBalance(self, user, amount) :     
     742 
     743    def decreaseUserAccountBalance(self, user, amount) : 
    733744        """Decreases user's account balance from an amount.""" 
    734745        self.doModify("UPDATE users SET balance=balance - %s WHERE id=%s" % (self.doQuote(amount), self.doQuote(user.ident))) 
    735         
     746 
    736747    def writeNewPayment(self, user, amount, comment="") : 
    737748        """Adds a new payment to the payments history.""" 
    738749        if user.ident is not None : 
    739750            self.doModify("INSERT INTO payments (userid, amount, description) VALUES (%s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(amount), self.doQuote(self.userCharsetToDatabase(comment)))) 
    740         else :     
     751        else : 
    741752            self.doModify("INSERT INTO payments (userid, amount, description) VALUES ((SELECT id FROM users WHERE username=%s), %s, %s)" % (self.doQuote(self.userCharsetToDatabase(user.Name)), self.doQuote(amount), self.doQuote(self.userCharsetToDatabase(comment)))) 
    742          
    743     def writeLastJobSize(self, lastjob, jobsize, jobprice) :         
     753 
     754    def writeLastJobSize(self, lastjob, jobsize, jobprice) : 
    744755        """Sets the last job's size permanently.""" 
    745756        self.doModify("UPDATE jobhistory SET jobsize=%s, jobprice=%s WHERE id=%s" % (self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(lastjob.ident))) 
    746          
     757 
    747758    def writeJobNew(self, printer, user, jobid, pagecounter, action, jobsize=None, jobprice=None, filename=None, title=None, copies=None, options=None, clienthost=None, jobsizebytes=None, jobmd5sum=None, jobpages=None, jobbilling=None, precomputedsize=None, precomputedprice=None) : 
    748759        """Adds a job in a printer's history.""" 
    749         if self.privacy :     
     760        if self.privacy : 
    750761            # For legal reasons, we want to hide the title, filename and options 
    751762            title = filename = options = "hidden" 
     
    757768            if jobsize is not None : 
    758769                self.doModify("INSERT INTO jobhistory (userid, printerid, jobid, pagecounter, action, jobsize, jobprice, filename, title, copies, options, hostname, jobsizebytes, md5sum, pages, billingcode, precomputedjobsize, precomputedjobprice) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(printer.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(clienthost), self.doQuote(jobsizebytes), self.doQuote(jobmd5sum), self.doQuote(jobpages), self.doQuote(jobbilling), self.doQuote(precomputedsize), self.doQuote(precomputedprice))) 
    759             else :     
     770            else : 
    760771                self.doModify("INSERT INTO jobhistory (userid, printerid, jobid, pagecounter, action, filename, title, copies, options, hostname, jobsizebytes, md5sum, pages, billingcode, precomputedjobsize, precomputedjobprice) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(printer.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(clienthost), self.doQuote(jobsizebytes), self.doQuote(jobmd5sum), self.doQuote(jobpages), self.doQuote(jobbilling), self.doQuote(precomputedsize), self.doQuote(precomputedprice))) 
    761         else :         
     772        else : 
    762773            # here we explicitly want to reset jobsize to NULL if needed 
    763774            self.doModify("UPDATE jobhistory SET userid=%s, jobid=%s, pagecounter=%s, action=%s, jobsize=%s, jobprice=%s, filename=%s, title=%s, copies=%s, options=%s, hostname=%s, jobsizebytes=%s, md5sum=%s, pages=%s, billingcode=%s, precomputedjobsize=%s, precomputedjobprice=%s, jobdate=now() WHERE id=%s" % (self.doQuote(user.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(clienthost), self.doQuote(jobsizebytes), self.doQuote(jobmd5sum), self.doQuote(jobpages), self.doQuote(jobbilling), self.doQuote(precomputedsize), self.doQuote(precomputedprice), self.doQuote(printer.LastJob.ident))) 
    764              
     775 
    765776    def saveUserPQuota(self, userpquota) : 
    766777        """Saves an user print quota entry.""" 
     
    774785                                 self.doQuote(userpquota.MaxJobSize), \ 
    775786                                 self.doQuote(userpquota.ident))) 
    776          
     787 
    777788    def writeUserPQuotaWarnCount(self, userpquota, warncount) : 
    778789        """Sets the warn counter value for a user quota.""" 
    779790        self.doModify("UPDATE userpquota SET warncount=%s WHERE id=%s" % (self.doQuote(warncount), self.doQuote(userpquota.ident))) 
    780          
     791 
    781792    def increaseUserPQuotaWarnCount(self, userpquota) : 
    782793        """Increases the warn counter value for a user quota.""" 
    783794        self.doModify("UPDATE userpquota SET warncount=warncount+1 WHERE id=%s" % self.doQuote(userpquota.ident)) 
    784          
     795 
    785796    def saveGroupPQuota(self, grouppquota) : 
    786797        """Saves a group print quota entry.""" 
     
    798809            for record in result : 
    799810                children.append(record.get("printerid")) # TODO : put this into the database integrity rules 
    800         if printer.ident not in children :         
     811        if printer.ident not in children : 
    801812            self.doModify("INSERT INTO printergroupsmembers (groupid, printerid) VALUES (%s, %s)" % (self.doQuote(pgroup.ident), self.doQuote(printer.ident))) 
    802          
     813 
    803814    def removePrinterFromGroup(self, pgroup, printer) : 
    804815        """Removes a printer from a printer group.""" 
    805816        self.doModify("DELETE FROM printergroupsmembers WHERE groupid=%s AND printerid=%s" % (self.doQuote(pgroup.ident), self.doQuote(printer.ident))) 
    806          
     817 
    807818    def retrieveHistory(self, user=None, printer=None, hostname=None, billingcode=None, jobid=None, limit=100, start=None, end=None) : 
    808819        """Retrieves all print jobs for user on printer (or all) between start and end date, limited to first 100 results.""" 
     
    813824        if printer is not None : # printer.ident is None anyway if printer doesn't exist 
    814825            where.append("printerid=%s" % self.doQuote(printer.ident)) 
    815         if hostname is not None :     
     826        if hostname is not None : 
    816827            where.append("hostname=%s" % self.doQuote(hostname)) 
    817         if billingcode is not None :     
     828        if billingcode is not None : 
    818829            where.append("billingcode=%s" % self.doQuote(self.userCharsetToDatabase(billingcode))) 
    819         if jobid is not None :     
     830        if jobid is not None : 
    820831            where.append("jobid=%s" % self.doQuote(jobid)) # TODO : jobid is text, so self.userCharsetToDatabase(jobid) but do all of them as well. 
    821         if start is not None :     
     832        if start is not None : 
    822833            where.append("jobdate>=%s" % self.doQuote(start)) 
    823         if end is not None :     
     834        if end is not None : 
    824835            where.append("jobdate<=%s" % self.doQuote(end)) 
    825         if where :     
     836        if where : 
    826837            query += " AND %s" % " AND ".join(where) 
    827838        query += " ORDER BY jobhistory.id DESC" 
    828839        if limit : 
    829840            query += " LIMIT %s" % self.doQuote(int(limit)) 
    830         jobs = []     
    831         result = self.doSearch(query)     
     841        jobs = [] 
     842        result = self.doSearch(query) 
    832843        if result : 
    833844            for fields in result : 
     
    835846                jobs.append(job) 
    836847        return jobs 
    837          
    838     def deleteUser(self, user) :     
     848 
     849    def deleteUser(self, user) : 
    839850        """Completely deletes an user from the database.""" 
    840851        # TODO : What should we do if we delete the last person who used a given printer ? 
    841852        # TODO : we can't reassign the last job to the previous one, because next user would be 
    842853        # TODO : incorrectly charged (overcharged). 
    843         for q in [  
     854        for q in [ 
    844855                    "DELETE FROM payments WHERE userid=%s" % self.doQuote(user.ident), 
    845856                    "DELETE FROM groupsmembers WHERE userid=%s" % self.doQuote(user.ident), 
     
    849860                  ] : 
    850861            self.doModify(q) 
    851              
    852     def multipleQueriesInTransaction(self, queries) :         
     862 
     863    def multipleQueriesInTransaction(self, queries) : 
    853864        """Does many modifications in a single transaction.""" 
    854865        self.beginTransaction() 
     
    856867            for q in queries : 
    857868                self.doModify(q) 
    858         except :     
     869        except : 
    859870            self.rollbackTransaction() 
    860871            raise 
    861         else :     
     872        else : 
    862873            self.commitTransaction() 
    863              
    864     def deleteManyBillingCodes(self, billingcodes) :         
     874 
     875    def deleteManyBillingCodes(self, billingcodes) : 
    865876        """Deletes many billing codes.""" 
    866877        codeids = ", ".join(["%s" % self.doQuote(b.ident) for b in billingcodes]) 
    867878        if codeids : 
    868             self.multipleQueriesInTransaction([  
     879            self.multipleQueriesInTransaction([ 
    869880                    "DELETE FROM billingcodes WHERE id IN (%s)" % codeids,]) 
    870              
    871     def deleteManyUsers(self, users) :         
     881 
     882    def deleteManyUsers(self, users) : 
    872883        """Deletes many users.""" 
    873884        userids = ", ".join(["%s" % self.doQuote(u.ident) for u in users]) 
    874885        if userids : 
    875             self.multipleQueriesInTransaction([  
     886            self.multipleQueriesInTransaction([ 
    876887                    "DELETE FROM payments WHERE userid IN (%s)" % userids, 
    877888                    "DELETE FROM groupsmembers WHERE userid IN (%s)" % userids, 
     
    879890                    "DELETE FROM userpquota WHERE userid IN (%s)" % userids, 
    880891                    "DELETE FROM users WHERE id IN (%s)" % userids,]) 
    881                      
    882     def deleteManyGroups(self, groups) :         
     892 
     893    def deleteManyGroups(self, groups) : 
    883894        """Deletes many groups.""" 
    884895        groupids = ", ".join(["%s" % self.doQuote(g.ident) for g in groups]) 
    885896        if groupids : 
    886             self.multipleQueriesInTransaction([  
     897            self.multipleQueriesInTransaction([ 
    887898                    "DELETE FROM groupsmembers WHERE groupid IN (%s)" % groupids, 
    888899                    "DELETE FROM grouppquota WHERE groupid IN (%s)" % groupids, 
    889900                    "DELETE FROM groups WHERE id IN (%s)" % groupids,]) 
    890          
     901 
    891902    def deleteManyPrinters(self, printers) : 
    892903        """Deletes many printers.""" 
    893904        printerids = ", ".join(["%s" % self.doQuote(p.ident) for p in printers]) 
    894905        if printerids : 
    895             self.multipleQueriesInTransaction([  
     906            self.multipleQueriesInTransaction([ 
    896907                    "DELETE FROM printergroupsmembers WHERE groupid IN (%s) OR printerid IN (%s)" % (printerids, printerids), 
    897908                    "DELETE FROM jobhistory WHERE printerid IN (%s)" % printerids, 
     
    899910                    "DELETE FROM userpquota WHERE printerid IN (%s)" % printerids, 
    900911                    "DELETE FROM printers WHERE id IN (%s)" % printerids,]) 
    901          
    902     def deleteManyUserPQuotas(self, printers, users) :         
     912 
     913    def deleteManyUserPQuotas(self, printers, users) : 
    903914        """Deletes many user print quota entries.""" 
    904915        printerids = ", ".join(["%s" % self.doQuote(p.ident) for p in printers]) 
    905916        userids = ", ".join(["%s" % self.doQuote(u.ident) for u in users]) 
    906917        if userids and printerids : 
    907             self.multipleQueriesInTransaction([  
     918            self.multipleQueriesInTransaction([ 
    908919                    "DELETE FROM jobhistory WHERE userid IN (%s) AND printerid IN (%s)" \ 
    909920                                 % (userids, printerids), 
    910921                    "DELETE FROM userpquota WHERE userid IN (%s) AND printerid IN (%s)" \ 
    911922                                 % (userids, printerids),]) 
    912              
     923 
    913924    def deleteManyGroupPQuotas(self, printers, groups) : 
    914925        """Deletes many group print quota entries.""" 
     
    916927        groupids = ", ".join(["%s" % self.doQuote(g.ident) for g in groups]) 
    917928        if groupids and printerids : 
    918             self.multipleQueriesInTransaction([  
     929            self.multipleQueriesInTransaction([ 
    919930                    "DELETE FROM grouppquota WHERE groupid IN (%s) AND printerid IN (%s)" \ 
    920931                                 % (groupids, printerids),]) 
    921          
    922     def deleteUserPQuota(self, upquota) :     
     932 
     933    def deleteUserPQuota(self, upquota) : 
    923934        """Completely deletes an user print quota entry from the database.""" 
    924         for q in [  
     935        for q in [ 
    925936                    "DELETE FROM jobhistory WHERE userid=%s AND printerid=%s" \ 
    926937                                 % (self.doQuote(upquota.User.ident), self.doQuote(upquota.Printer.ident)), 
     
    928939                  ] : 
    929940            self.doModify(q) 
    930          
    931     def deleteGroupPQuota(self, gpquota) :     
     941 
     942    def deleteGroupPQuota(self, gpquota) : 
    932943        """Completely deletes a group print quota entry from the database.""" 
    933         for q in [  
     944        for q in [ 
    934945                    "DELETE FROM grouppquota WHERE id=%s" % self.doQuote(gpquota.ident), 
    935946                  ] : 
    936947            self.doModify(q) 
    937          
    938     def deleteGroup(self, group) :     
     948 
     949    def deleteGroup(self, group) : 
    939950        """Completely deletes a group from the database.""" 
    940951        for q in [ 
     
    942953                   "DELETE FROM grouppquota WHERE groupid=%s" % self.doQuote(group.ident), 
    943954                   "DELETE FROM groups WHERE id=%s" % self.doQuote(group.ident), 
    944                  ] :   
     955                 ] : 
    945956            self.doModify(q) 
    946              
    947     def deletePrinter(self, printer) :     
     957 
     958    def deletePrinter(self, printer) : 
    948959        """Completely deletes a printer from the database.""" 
    949         for q in [  
     960        for q in [ 
    950961                    "DELETE FROM printergroupsmembers WHERE groupid=%s OR printerid=%s" % (self.doQuote(printer.ident), self.doQuote(printer.ident)), 
    951962                    "DELETE FROM jobhistory WHERE printerid=%s" % self.doQuote(printer.ident), 
     
    955966                  ] : 
    956967            self.doModify(q) 
    957              
    958     def deleteBillingCode(self, code) :     
     968 
     969    def deleteBillingCode(self, code) : 
    959970        """Completely deletes a billing code from the database.""" 
    960971        for q in [ 
    961972                   "DELETE FROM billingcodes WHERE id=%s" % self.doQuote(code.ident), 
    962                  ] :   
     973                 ] : 
    963974            self.doModify(q) 
    964          
     975