root / pykota / trunk / pykota / storages / sql.py @ 3561

Revision 3561, 55.9 kB (checked in by jerome, 11 years ago)

Changed copyright years.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1# -*- coding: utf-8 -*-
2#
3# PyKota : Print Quotas for CUPS
4#
5# (c) 2003-2013 Jerome Alet <alet@librelogiciel.com>
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18#
19# $Id$
20#
21#
22
23"""This module defines methods common to all relational backends."""
24
25from pykota.storage import StorageUser, StorageGroup, StoragePrinter, \
26                           StorageJob, StorageLastJob, StorageUserPQuota, \
27                           StorageGroupPQuota, StorageBillingCode
28
29from pykota.utils import *
30
31MAXINNAMES = 500 # Maximum number of non-patterns names to use in a single IN statement
32
33class SQLStorage :
34    def storageUserFromRecord(self, username, record) :
35        """Returns a StorageUser instance from a database record."""
36        user = StorageUser(self, username)
37        user.ident = record.get("uid", record.get("userid", record.get("id")))
38        user.LimitBy = databaseToUnicode(record.get("limitby") or "quota")
39        user.AccountBalance = record.get("balance")
40        user.LifeTimePaid = record.get("lifetimepaid")
41        user.Email = databaseToUnicode(record.get("email"))
42        user.Description = databaseToUnicode(record.get("description"))
43        user.OverCharge = record.get("overcharge", 1.0)
44        user.Exists = True
45        return user
46
47    def storageGroupFromRecord(self, groupname, record) :
48        """Returns a StorageGroup instance from a database record."""
49        group = StorageGroup(self, groupname)
50        group.ident = record.get("id")
51        group.LimitBy = databaseToUnicode(record.get("limitby") or "quota")
52        group.AccountBalance = record.get("balance")
53        group.LifeTimePaid = record.get("lifetimepaid")
54        group.Description = databaseToUnicode(record.get("description"))
55        group.Exists = True
56        return group
57
58    def storagePrinterFromRecord(self, printername, record) :
59        """Returns a StoragePrinter instance from a database record."""
60        printer = StoragePrinter(self, printername)
61        printer.ident = record.get("id")
62        printer.PricePerJob = record.get("priceperjob") or 0.0
63        printer.PricePerPage = record.get("priceperpage") or 0.0
64        printer.MaxJobSize = record.get("maxjobsize")
65        printer.PassThrough = record.get("passthrough") or 0
66        if printer.PassThrough in (1, "1", "t", "true", "TRUE", "True") :
67            printer.PassThrough = True
68        else :
69            printer.PassThrough = False
70        printer.Description = databaseToUnicode(record.get("description") or "") # TODO : is 'or ""' still needed ?
71        printer.Exists = True
72        return printer
73
74    def setJobAttributesFromRecord(self, job, record) :
75        """Sets the attributes of a job from a database record."""
76        job.ident = record.get("id")
77        job.JobId = databaseToUnicode(record.get("jobid"))
78        job.PrinterPageCounter = record.get("pagecounter")
79        job.JobSize = record.get("jobsize")
80        job.JobPrice = record.get("jobprice")
81        job.JobAction = record.get("action")
82        job.JobFileName = databaseToUnicode(record.get("filename") or "")
83        job.JobTitle = databaseToUnicode(record.get("title") or "")
84        job.JobCopies = record.get("copies")
85        job.JobOptions = databaseToUnicode(record.get("options") or "")
86        job.JobDate = record.get("jobdate")
87        job.JobHostName = databaseToUnicode(record.get("hostname"))
88        job.JobSizeBytes = record.get("jobsizebytes")
89        job.JobMD5Sum = record.get("md5sum")
90        job.JobPages = record.get("pages")
91        job.JobBillingCode = databaseToUnicode(record.get("billingcode") or "")
92        job.PrecomputedJobSize = record.get("precomputedjobsize")
93        job.PrecomputedJobPrice = record.get("precomputedjobprice")
94        job.UserName = databaseToUnicode(record.get("username"))
95        job.PrinterName = databaseToUnicode(record.get("printername"))
96        if job.JobTitle == job.JobFileName == job.JobOptions == "hidden" :
97            (job.JobTitle, job.JobFileName, job.JobOptions) = (_("Hidden because of privacy concerns"),) * 3
98        job.Exists = True
99
100    def storageJobFromRecord(self, record) :
101        """Returns a StorageJob instance from a database record."""
102        job = StorageJob(self)
103        self.setJobAttributesFromRecord(job, record)
104        return job
105
106    def storageLastJobFromRecord(self, printer, record) :
107        """Returns a StorageLastJob instance from a database record."""
108        lastjob = StorageLastJob(self, printer)
109        self.setJobAttributesFromRecord(lastjob, record)
110        return lastjob
111
112    def storageUserPQuotaFromRecord(self, user, printer, record) :
113        """Returns a StorageUserPQuota instance from a database record."""
114        userpquota = StorageUserPQuota(self, user, printer)
115        userpquota.ident = record.get("id")
116        userpquota.PageCounter = record.get("pagecounter")
117        userpquota.LifePageCounter = record.get("lifepagecounter")
118        userpquota.SoftLimit = record.get("softlimit")
119        userpquota.HardLimit = record.get("hardlimit")
120        userpquota.DateLimit = record.get("datelimit")
121        userpquota.MaxJobSize = record.get("maxjobsize")
122        userpquota.WarnCount = record.get("warncount") or 0
123        userpquota.Exists = True
124        return userpquota
125
126    def storageGroupPQuotaFromRecord(self, group, printer, record) :
127        """Returns a StorageGroupPQuota instance from a database record."""
128        grouppquota = StorageGroupPQuota(self, group, printer)
129        grouppquota.ident = record.get("id")
130        grouppquota.SoftLimit = record.get("softlimit")
131        grouppquota.HardLimit = record.get("hardlimit")
132        grouppquota.DateLimit = record.get("datelimit")
133        result = self.doSearch("SELECT SUM(lifepagecounter) AS lifepagecounter, SUM(pagecounter) AS pagecounter FROM userpquota WHERE printerid=%s AND userid IN (SELECT userid FROM groupsmembers WHERE groupid=%s)" \
134                      % (self.doQuote(printer.ident), self.doQuote(group.ident)))
135        if result :
136            grouppquota.PageCounter = result[0].get("pagecounter") or 0
137            grouppquota.LifePageCounter = result[0].get("lifepagecounter") or 0
138        grouppquota.Exists = True
139        return grouppquota
140
141    def storageBillingCodeFromRecord(self, billingcode, record) :
142        """Returns a StorageBillingCode instance from a database record."""
143        code = StorageBillingCode(self, billingcode)
144        code.ident = record.get("id")
145        code.Description = databaseToUnicode(record.get("description") or "") # TODO : is 'or ""' still needed ?
146        code.Balance = record.get("balance") or 0.0
147        code.PageCounter = record.get("pagecounter") or 0
148        code.Exists = True
149        return code
150
151    def createFilter(self, only) :
152        """Returns the appropriate SQL filter."""
153        if only :
154            expressions = []
155            for (k, v) in only.items() :
156                expressions.append("%s=%s" % (k, self.doQuote(unicodeToDatabase(v))))
157            return " AND ".join(expressions)
158        return ""
159
160    def createOrderBy(self, default, ordering) :
161        """Creates a suitable ORDER BY statement based on a list of fieldnames prefixed with '+' (ASC) or '-' (DESC)."""
162        statements = []
163        if not ordering :
164            ordering = default
165        for field in ordering :
166            if field.startswith("-") :
167                statements.append("%s DESC" % field[1:])
168            elif field.startswith("+") :
169                statements.append("%s ASC" % field[1:])
170            else :
171                statements.append("%s ASC" % field)
172        return ", ".join(statements)
173
174    def extractPrinters(self, extractonly={}, ordering=[]) :
175        """Extracts all printer records."""
176        thefilter = self.createFilter(extractonly)
177        if thefilter :
178            thefilter = "WHERE %s" % thefilter
179        orderby = self.createOrderBy(["+id"], ordering)
180        result = self.doRawSearch("SELECT * FROM printers %(thefilter)s ORDER BY %(orderby)s" % locals())
181        return self.prepareRawResult(result)
182
183    def extractUsers(self, extractonly={}, ordering=[]) :
184        """Extracts all user records."""
185        thefilter = self.createFilter(extractonly)
186        if thefilter :
187            thefilter = "WHERE %s" % thefilter
188        orderby = self.createOrderBy(["+id"], ordering)
189        result = self.doRawSearch("SELECT * FROM users %(thefilter)s ORDER BY %(orderby)s" % locals())
190        return self.prepareRawResult(result)
191
192    def extractBillingcodes(self, extractonly={}, ordering=[]) :
193        """Extracts all billing codes records."""
194        thefilter = self.createFilter(extractonly)
195        if thefilter :
196            thefilter = "WHERE %s" % thefilter
197        orderby = self.createOrderBy(["+id"], ordering)
198        result = self.doRawSearch("SELECT * FROM billingcodes %(thefilter)s ORDER BY %(orderby)s" % locals())
199        return self.prepareRawResult(result)
200
201    def extractGroups(self, extractonly={}, ordering=[]) :
202        """Extracts all group records."""
203        thefilter = self.createFilter(extractonly)
204        if thefilter :
205            thefilter = "WHERE %s" % thefilter
206        orderby = self.createOrderBy(["+groups.id"], ordering)
207        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())
208        return self.prepareRawResult(result)
209
210    def extractPayments(self, extractonly={}, ordering=[]) :
211        """Extracts all payment records."""
212        startdate = extractonly.get("start")
213        enddate = extractonly.get("end")
214        for limit in ("start", "end") :
215            try :
216                del extractonly[limit]
217            except KeyError :
218                pass
219        thefilter = self.createFilter(extractonly)
220        if thefilter :
221            thefilter = "AND %s" % thefilter
222        (startdate, enddate) = self.cleanDates(startdate, enddate)
223        if startdate :
224            thefilter = "%s AND date>=%s" % (thefilter, self.doQuote(startdate))
225        if enddate :
226            thefilter = "%s AND date<=%s" % (thefilter, self.doQuote(enddate))
227        orderby = self.createOrderBy(["+payments.id"], ordering)
228        result = self.doRawSearch("SELECT username,payments.* FROM users,payments WHERE users.id=payments.userid %(thefilter)s ORDER BY %(orderby)s" % locals())
229        return self.prepareRawResult(result)
230
231    def extractUpquotas(self, extractonly={}, ordering=[]) :
232        """Extracts all userpquota records."""
233        thefilter = self.createFilter(extractonly)
234        if thefilter :
235            thefilter = "AND %s" % thefilter
236        orderby = self.createOrderBy(["+userpquota.id"], ordering)
237        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())
238        return self.prepareRawResult(result)
239
240    def extractGpquotas(self, extractonly={}, ordering=[]) :
241        """Extracts all grouppquota records."""
242        thefilter = self.createFilter(extractonly)
243        if thefilter :
244            thefilter = "AND %s" % thefilter
245        orderby = self.createOrderBy(["+grouppquota.id"], ordering)
246        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,groups.groupname,printers.printername ORDER BY %(orderby)s" % locals())
247        return self.prepareRawResult(result)
248
249    def extractUmembers(self, extractonly={}, ordering=[]) :
250        """Extracts all user groups members."""
251        thefilter = self.createFilter(extractonly)
252        if thefilter :
253            thefilter = "AND %s" % thefilter
254        orderby = self.createOrderBy(["+groupsmembers.groupid", "+groupsmembers.userid"], ordering)
255        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())
256        return self.prepareRawResult(result)
257
258    def extractPmembers(self, extractonly={}, ordering=[]) :
259        """Extracts all printer groups members."""
260        for (k, v) in extractonly.items() :
261            if k == "pgroupname" :
262                del extractonly[k]
263                extractonly["p1.printername"] = v
264            elif k == "printername" :
265                del extractonly[k]
266                extractonly["p2.printername"] = v
267        thefilter = self.createFilter(extractonly)
268        if thefilter :
269            thefilter = "AND %s" % thefilter
270        orderby = self.createOrderBy(["+printergroupsmembers.groupid", "+printergroupsmembers.printerid"], ordering)
271        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())
272        return self.prepareRawResult(result)
273
274    def extractHistory(self, extractonly={}, ordering=[]) :
275        """Extracts all jobhistory records."""
276        startdate = extractonly.get("start")
277        enddate = extractonly.get("end")
278        for limit in ("start", "end") :
279            try :
280                del extractonly[limit]
281            except KeyError :
282                pass
283        thefilter = self.createFilter(extractonly)
284        if thefilter :
285            thefilter = "AND %s" % thefilter
286        (startdate, enddate) = self.cleanDates(startdate, enddate)
287        if startdate :
288            thefilter = "%s AND jobdate>=%s" % (thefilter, self.doQuote(startdate))
289        if enddate :
290            thefilter = "%s AND jobdate<=%s" % (thefilter, self.doQuote(enddate))
291        orderby = self.createOrderBy(["+jobhistory.id"], ordering)
292        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())
293        return self.prepareRawResult(result)
294
295    def filterNames(self, records, attribute, patterns=None) :
296        """Returns a list of 'attribute' from a list of records.
297
298           Logs any missing attribute.
299        """
300        result = []
301        for record in records :
302            attrval = record.get(attribute, [None])
303            if attrval is None :
304                self.tool.printInfo("Object %s has no %s attribute !" % (repr(record), attribute), "error")
305            else :
306                attrval = databaseToUnicode(attrval)
307                if patterns :
308                    if (not isinstance(patterns, type([]))) and (not isinstance(patterns, type(()))) :
309                        patterns = [ patterns ]
310                    if self.tool.matchString(attrval, patterns) :
311                        result.append(attrval)
312                else :
313                    result.append(attrval)
314        return result
315
316    def getAllBillingCodes(self, billingcode=None) :
317        """Extracts all billing codes or only the billing codes matching the optional parameter."""
318        result = self.doSearch("SELECT billingcode FROM billingcodes")
319        if result :
320            return self.filterNames(result, "billingcode", billingcode)
321        else :
322            return []
323
324    def getAllPrintersNames(self, printername=None) :
325        """Extracts all printer names or only the printers' names matching the optional parameter."""
326        result = self.doSearch("SELECT printername FROM printers")
327        if result :
328            return self.filterNames(result, "printername", printername)
329        else :
330            return []
331
332    def getAllUsersNames(self, username=None) :
333        """Extracts all user names."""
334        result = self.doSearch("SELECT username FROM users")
335        if result :
336            return self.filterNames(result, "username", username)
337        else :
338            return []
339
340    def getAllGroupsNames(self, groupname=None) :
341        """Extracts all group names."""
342        result = self.doSearch("SELECT groupname FROM groups")
343        if result :
344            return self.filterNames(result, "groupname", groupname)
345        else :
346            return []
347
348    def getUserNbJobsFromHistory(self, user) :
349        """Returns the number of jobs the user has in history."""
350        result = self.doSearch("SELECT COUNT(*) AS count FROM jobhistory WHERE userid=%s" % self.doQuote(user.ident))
351        if result :
352            return result[0]["count"]
353        return 0
354
355    def getUserFromBackend(self, username) :
356        """Extracts user information given its name."""
357        result = self.doSearch("SELECT * FROM users WHERE username=%s"\
358                      % self.doQuote(unicodeToDatabase(username)))
359        if result :
360            return self.storageUserFromRecord(username, result[0])
361        else :
362            return StorageUser(self, username)
363
364    def getGroupFromBackend(self, groupname) :
365        """Extracts group information given its name."""
366        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" \
367                      % self.doQuote(unicodeToDatabase(groupname)))
368        if result :
369            return self.storageGroupFromRecord(groupname, result[0])
370        else :
371            return StorageGroup(self, groupname)
372
373    def getPrinterFromBackend(self, printername) :
374        """Extracts printer information given its name."""
375        result = self.doSearch("SELECT * FROM printers WHERE printername=%s" \
376                      % self.doQuote(unicodeToDatabase(printername)))
377        if result :
378            return self.storagePrinterFromRecord(printername, result[0])
379        else :
380            return StoragePrinter(self, printername)
381
382    def getBillingCodeFromBackend(self, label) :
383        """Extracts a billing code information given its name."""
384        result = self.doSearch("SELECT * FROM billingcodes WHERE billingcode=%s" \
385                      % self.doQuote(unicodeToDatabase(label)))
386        if result :
387            return self.storageBillingCodeFromRecord(label, result[0])
388        else :
389            return StorageBillingCode(self, label)
390
391    def getUserPQuotaFromBackend(self, user, printer) :
392        """Extracts a user print quota."""
393        if printer.Exists and user.Exists :
394            result = self.doSearch("SELECT * FROM userpquota WHERE userid=%s AND printerid=%s;" \
395                          % (self.doQuote(user.ident), self.doQuote(printer.ident)))
396            if result :
397                return self.storageUserPQuotaFromRecord(user, printer, result[0])
398        return StorageUserPQuota(self, user, printer)
399
400    def getGroupPQuotaFromBackend(self, group, printer) :
401        """Extracts a group print quota."""
402        if printer.Exists and group.Exists :
403            result = self.doSearch("SELECT * FROM grouppquota WHERE groupid=%s AND printerid=%s" \
404                          % (self.doQuote(group.ident), self.doQuote(printer.ident)))
405            if result :
406                return self.storageGroupPQuotaFromRecord(group, printer, result[0])
407        return StorageGroupPQuota(self, group, printer)
408
409    def getPrinterLastJobFromBackend(self, printer) :
410        """Extracts a printer's last job information."""
411        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 userid=users.id AND jobhistory.id IN (SELECT max(id) FROM jobhistory WHERE printerid=%s)" % self.doQuote(printer.ident))
412        if result :
413            return self.storageLastJobFromRecord(printer, result[0])
414        else :
415            return StorageLastJob(self, printer)
416
417    def getGroupMembersFromBackend(self, group) :
418        """Returns the group's members list."""
419        groupmembers = []
420        result = self.doSearch("SELECT * FROM groupsmembers JOIN users ON groupsmembers.userid=users.id WHERE groupid=%s" % self.doQuote(group.ident))
421        if result :
422            for record in result :
423                user = self.storageUserFromRecord(databaseToUnicode(record.get("username")), \
424                                                  record)
425                groupmembers.append(user)
426                self.cacheEntry("USERS", user.Name, user)
427        return groupmembers
428
429    def getUserGroupsFromBackend(self, user) :
430        """Returns the user's groups list."""
431        groups = []
432        result = self.doSearch("SELECT groupname FROM groupsmembers JOIN groups ON groupsmembers.groupid=groups.id WHERE userid=%s" % self.doQuote(user.ident))
433        if result :
434            for record in result :
435                groups.append(self.getGroup(databaseToUnicode(record.get("groupname"))))
436        return groups
437
438    def getParentPrintersFromBackend(self, printer) :
439        """Get all the printer groups this printer is a member of."""
440        pgroups = []
441        result = self.doSearch("SELECT groupid,printername FROM printergroupsmembers JOIN printers ON groupid=id WHERE printerid=%s" % self.doQuote(printer.ident))
442        if result :
443            for record in result :
444                if record["groupid"] != printer.ident : # in case of integrity violation
445                    parentprinter = self.getPrinter(databaseToUnicode(record.get("printername")))
446                    if parentprinter.Exists :
447                        pgroups.append(parentprinter)
448        return pgroups
449
450    def getMatchingStuff(self, pattern, tablename, entrytype, keyname) :
451        """Returns the list of all entries for which the name matches a certain pattern."""
452        entries = []
453        # We 'could' do a SELECT xxxxname FROM xxxx WHERE xxxxname LIKE ...
454        # but we don't because other storages semantics may be different, so every
455        # storage should use fnmatch to match patterns and be storage agnostic
456        #
457        # This doesn't prevent us from being smarter, thanks to bse@chalmers.se
458        pattern = pattern or "*"
459        patterns = pattern.split(",")
460        patdict = {}.fromkeys(patterns)
461        patterns = patdict.keys() # Ensures the uniqueness of each pattern, but we lose the cmd line ordering
462        # BEWARE : if a single pattern contains wild cards, we'll still use the slow route.
463        storageEntryFromRecord = getattr(self, "storage%sFromRecord" % entrytype)
464        cachename = tablename.upper()
465        if self.hasWildCards(pattern) :
466            # Slow route
467            result = self.doSearch("SELECT * FROM %s" % tablename)
468            if result :
469                for record in result :
470                    name = databaseToUnicode(record[keyname])
471                    if patdict.has_key(name) or self.tool.matchString(name, patterns) :
472                        entry = storageEntryFromRecord(name, record)
473                        entries.append(entry)
474                        self.cacheEntry(cachename, entry.Name, entry)
475        else :
476            # Fast route (probably not faster with a few entries)
477            while patterns :
478                subset = patterns[:MAXINNAMES]
479                nbpatterns = len(subset)
480                if nbpatterns == 1 :
481                    wherestmt = "%s=%s" % (keyname, self.doQuote(unicodeToDatabase(subset[0])))
482                else :
483                    wherestmt = "%s IN (%s)" % (keyname,
484                                                ",".join([self.doQuote(unicodeToDatabase(p)) for p in subset]))
485                result = self.doSearch("SELECT * FROM %s WHERE %s" % (tablename,
486                                                                      wherestmt))
487                if result :
488                    for record in result :
489                        name = databaseToUnicode(record[keyname])
490                        entry = storageEntryFromRecord(name, record)
491                        entries.append(entry)
492                        self.cacheEntry(cachename, entry.Name, entry)
493                patterns = patterns[MAXINNAMES:]
494        entries.sort(key=lambda e : e.Name) # Adds some ordering, we've already lost the cmd line one anyway.
495        return entries
496
497    def getMatchingPrinters(self, pattern) :
498        """Returns the list of all printers for which name matches a certain pattern."""
499        return self.getMatchingStuff(pattern, "printers", "Printer", "printername")
500
501    def getMatchingUsers(self, pattern) :
502        """Returns the list of all users for which name matches a certain pattern."""
503        return self.getMatchingStuff(pattern, "users", "User", "username")
504
505    def getMatchingBillingCodes(self, pattern) :
506        """Returns the list of all billing codes for which the label matches a certain pattern."""
507        return self.getMatchingStuff(pattern, "billingcodes", "BillingCode", "billingcode")
508
509    def getMatchingGroups(self, grouppattern) :
510        """Returns the list of all groups for which name matches a certain pattern."""
511        groups = []
512        # We 'could' do a SELECT groupname FROM groups WHERE groupname LIKE ...
513        # but we don't because other storages semantics may be different, so every
514        # storage should use fnmatch to match patterns and be storage agnostic
515        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) GROUP BY groups.id,groups.groupname,groups.limitby,groups.description")
516        if result :
517            patterns = grouppattern.split(",")
518            patdict = {}.fromkeys(patterns)
519            for record in result :
520                gname = databaseToUnicode(record["groupname"])
521                if patdict.has_key(gname) or self.tool.matchString(gname, patterns) :
522                    group = self.storageGroupFromRecord(gname, record)
523                    groups.append(group)
524                    self.cacheEntry("GROUPS", group.Name, group)
525        return groups
526
527    def getPrinterUsersAndQuotas(self, printer, names=["*"]) :
528        """Returns the list of users who uses a given printer, along with their quotas."""
529        usersandquotas = []
530        result = self.doSearch("SELECT users.id as uid,username,description,balance,lifetimepaid,limitby,email,overcharge,userpquota.id,lifepagecounter,pagecounter,softlimit,hardlimit,datelimit,warncount FROM users JOIN userpquota ON users.id=userpquota.userid AND printerid=%s ORDER BY username ASC" % self.doQuote(printer.ident))
531        if result :
532            for record in result :
533                uname = databaseToUnicode(record.get("username"))
534                if self.tool.matchString(uname, names) :
535                    user = self.storageUserFromRecord(uname, record)
536                    userpquota = self.storageUserPQuotaFromRecord(user, printer, record)
537                    usersandquotas.append((user, userpquota))
538                    self.cacheEntry("USERS", user.Name, user)
539                    self.cacheEntry("USERPQUOTAS", "%s@%s" % (user.Name, printer.Name), userpquota)
540        return usersandquotas
541
542    def getPrinterGroupsAndQuotas(self, printer, names=["*"]) :
543        """Returns the list of groups which uses a given printer, along with their quotas."""
544        groupsandquotas = []
545        result = self.doSearch("SELECT groupname FROM groups JOIN grouppquota ON groups.id=grouppquota.groupid AND printerid=%s ORDER BY groupname ASC" % self.doQuote(printer.ident))
546        if result :
547            for record in result :
548                gname = databaseToUnicode(record.get("groupname"))
549                if self.tool.matchString(gname, names) :
550                    group = self.getGroup(gname)
551                    grouppquota = self.getGroupPQuota(group, printer)
552                    groupsandquotas.append((group, grouppquota))
553        return groupsandquotas
554
555    def addPrinter(self, printer) :
556        """Adds a printer to the quota storage, returns the old value if it already exists."""
557        oldentry = self.getPrinter(printer.Name)
558        if oldentry.Exists :
559            return oldentry
560        self.doModify("INSERT INTO printers (printername, passthrough, maxjobsize, description, priceperpage, priceperjob) VALUES (%s, %s, %s, %s, %s, %s)" \
561                          % (self.doQuote(unicodeToDatabase(printer.Name)), \
562                             self.doQuote((printer.PassThrough and "t") or "f"), \
563                             self.doQuote(printer.MaxJobSize), \
564                             self.doQuote(unicodeToDatabase(printer.Description)), \
565                             self.doQuote(printer.PricePerPage or 0.0), \
566                             self.doQuote(printer.PricePerJob or 0.0)))
567        printer.isDirty = False
568        return None # the entry created doesn't need further modification
569
570    def addBillingCode(self, bcode) :
571        """Adds a billing code to the quota storage, returns the old value if it already exists."""
572        oldentry = self.getBillingCode(bcode.BillingCode)
573        if oldentry.Exists :
574            return oldentry
575        self.doModify("INSERT INTO billingcodes (billingcode, balance, pagecounter, description) VALUES (%s, %s, %s, %s)" \
576                           % (self.doQuote(unicodeToDatabase(bcode.BillingCode)),
577                              self.doQuote(bcode.Balance or 0.0), \
578                              self.doQuote(bcode.PageCounter or 0), \
579                              self.doQuote(unicodeToDatabase(bcode.Description))))
580        bcode.isDirty = False
581        return None # the entry created doesn't need further modification
582
583    def addUser(self, user) :
584        """Adds a user to the quota storage, returns the old value if it already exists."""
585        oldentry = self.getUser(user.Name)
586        if oldentry.Exists :
587            return oldentry
588        self.doModify("INSERT INTO users (username, limitby, balance, lifetimepaid, email, overcharge, description) VALUES (%s, %s, %s, %s, %s, %s, %s)" % \
589                                     (self.doQuote(unicodeToDatabase(user.Name)), \
590                                      self.doQuote(unicodeToDatabase(user.LimitBy or 'quota')), \
591                                      self.doQuote(user.AccountBalance or 0.0), \
592                                      self.doQuote(user.LifeTimePaid or 0.0), \
593                                      self.doQuote(unicodeToDatabase(user.Email)), \
594                                      self.doQuote(user.OverCharge), \
595                                      self.doQuote(unicodeToDatabase(user.Description))))
596        if user.PaymentsBacklog :
597            for (value, comment) in user.PaymentsBacklog :
598                self.writeNewPayment(user, value, comment)
599            user.PaymentsBacklog = []
600        user.isDirty = False
601        return None # the entry created doesn't need further modification
602
603    def addGroup(self, group) :
604        """Adds a group to the quota storage, returns the old value if it already exists."""
605        oldentry = self.getGroup(group.Name)
606        if oldentry.Exists :
607            return oldentry
608        self.doModify("INSERT INTO groups (groupname, limitby, description) VALUES (%s, %s, %s)" % \
609                              (self.doQuote(unicodeToDatabase(group.Name)), \
610                               self.doQuote(unicodeToDatabase(group.LimitBy or "quota")), \
611                               self.doQuote(unicodeToDatabase(group.Description))))
612        group.isDirty = False
613        return None # the entry created doesn't need further modification
614
615    def addUserToGroup(self, user, group) :
616        """Adds an user to a group."""
617        result = self.doSearch("SELECT COUNT(*) AS mexists FROM groupsmembers WHERE groupid=%s AND userid=%s" % (self.doQuote(group.ident), self.doQuote(user.ident)))
618        try :
619            mexists = int(result[0].get("mexists"))
620        except (IndexError, TypeError) :
621            mexists = 0
622        if not mexists :
623            self.doModify("INSERT INTO groupsmembers (groupid, userid) VALUES (%s, %s)" % (self.doQuote(group.ident), self.doQuote(user.ident)))
624
625    def delUserFromGroup(self, user, group) :
626        """Removes an user from a group."""
627        self.doModify("DELETE FROM groupsmembers WHERE groupid=%s AND userid=%s" % \
628                       (self.doQuote(group.ident), self.doQuote(user.ident)))
629
630    def addUserPQuota(self, upq) :
631        """Initializes a user print quota on a printer."""
632        oldentry = self.getUserPQuota(upq.User, upq.Printer)
633        if oldentry.Exists :
634            return oldentry
635        self.doModify("INSERT INTO userpquota (userid, printerid, softlimit, hardlimit, warncount, datelimit, pagecounter, lifepagecounter, maxjobsize) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)" \
636                          % (self.doQuote(upq.User.ident), \
637                             self.doQuote(upq.Printer.ident), \
638                             self.doQuote(upq.SoftLimit), \
639                             self.doQuote(upq.HardLimit), \
640                             self.doQuote(upq.WarnCount or 0), \
641                             self.doQuote(upq.DateLimit), \
642                             self.doQuote(upq.PageCounter or 0), \
643                             self.doQuote(upq.LifePageCounter or 0), \
644                             self.doQuote(upq.MaxJobSize)))
645        upq.isDirty = False
646        return None # the entry created doesn't need further modification
647
648    def addGroupPQuota(self, gpq) :
649        """Initializes a group print quota on a printer."""
650        oldentry = self.getGroupPQuota(gpq.Group, gpq.Printer)
651        if oldentry.Exists :
652            return oldentry
653        self.doModify("INSERT INTO grouppquota (groupid, printerid, softlimit, hardlimit, datelimit) VALUES (%s, %s, %s, %s, %s)" \
654                          % (self.doQuote(gpq.Group.ident), \
655                             self.doQuote(gpq.Printer.ident), \
656                             self.doQuote(gpq.SoftLimit), \
657                             self.doQuote(gpq.HardLimit), \
658                             self.doQuote(gpq.DateLimit)))
659        gpq.isDirty = False
660        return None # the entry created doesn't need further modification
661
662    def savePrinter(self, printer) :
663        """Saves the printer to the database in a single operation."""
664        self.doModify("UPDATE printers SET passthrough=%s, maxjobsize=%s, description=%s, priceperpage=%s, priceperjob=%s WHERE id=%s" \
665                              % (self.doQuote((printer.PassThrough and "t") or "f"), \
666                                 self.doQuote(printer.MaxJobSize), \
667                                 self.doQuote(unicodeToDatabase(printer.Description)), \
668                                 self.doQuote(printer.PricePerPage or 0.0), \
669                                 self.doQuote(printer.PricePerJob or 0.0), \
670                                 self.doQuote(printer.ident)))
671
672    def saveUser(self, user) :
673        """Saves the user to the database in a single operation."""
674        self.doModify("UPDATE users SET limitby=%s, balance=%s, lifetimepaid=%s, email=%s, overcharge=%s, description=%s WHERE id=%s" \
675                               % (self.doQuote(unicodeToDatabase(user.LimitBy or 'quota')), \
676                                  self.doQuote(user.AccountBalance or 0.0), \
677                                  self.doQuote(user.LifeTimePaid or 0.0), \
678                                  self.doQuote(unicodeToDatabase(user.Email)), \
679                                  self.doQuote(user.OverCharge), \
680                                  self.doQuote(unicodeToDatabase(user.Description)), \
681                                  self.doQuote(user.ident)))
682
683    def saveGroup(self, group) :
684        """Saves the group to the database in a single operation."""
685        self.doModify("UPDATE groups SET limitby=%s, description=%s WHERE id=%s" \
686                               % (self.doQuote(unicodeToDatabase(group.LimitBy or 'quota')), \
687                                  self.doQuote(unicodeToDatabase(group.Description)), \
688                                  self.doQuote(group.ident)))
689
690    def writeUserPQuotaDateLimit(self, userpquota, datelimit) :
691        """Sets the date limit permanently for a user print quota."""
692        self.doModify("UPDATE userpquota SET datelimit=%s WHERE id=%s" % (self.doQuote(datelimit), self.doQuote(userpquota.ident)))
693
694    def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) :
695        """Sets the date limit permanently for a group print quota."""
696        self.doModify("UPDATE grouppquota SET datelimit=%s WHERE id=%s" % (self.doQuote(datelimit), self.doQuote(grouppquota.ident)))
697
698    def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) :
699        """Increase page counters for a user print quota."""
700        self.doModify("UPDATE userpquota SET pagecounter=pagecounter + %s,lifepagecounter=lifepagecounter + %s WHERE id=%s" % (self.doQuote(nbpages), self.doQuote(nbpages), self.doQuote(userpquota.ident)))
701
702    def saveBillingCode(self, bcode) :
703        """Saves the billing code to the database."""
704        self.doModify("UPDATE billingcodes SET balance=%s, pagecounter=%s, description=%s WHERE id=%s" \
705                            % (self.doQuote(bcode.Balance or 0.0), \
706                               self.doQuote(bcode.PageCounter or 0), \
707                               self.doQuote(unicodeToDatabase(bcode.Description)), \
708                               self.doQuote(bcode.ident)))
709
710    def consumeBillingCode(self, bcode, pagecounter, balance) :
711        """Consumes from a billing code."""
712        self.doModify("UPDATE billingcodes SET balance=balance + %s, pagecounter=pagecounter + %s WHERE id=%s" % (self.doQuote(balance), self.doQuote(pagecounter), self.doQuote(bcode.ident)))
713
714    def refundJob(self, jobident) :
715        """Marks a job as refunded in the history."""
716        self.doModify("UPDATE jobhistory SET action='REFUND' WHERE id=%s;" % self.doQuote(jobident))
717
718    def decreaseUserAccountBalance(self, user, amount) :
719        """Decreases user's account balance from an amount."""
720        self.doModify("UPDATE users SET balance=balance - %s WHERE id=%s" % (self.doQuote(amount), self.doQuote(user.ident)))
721
722    def writeNewPayment(self, user, amount, comment="") :
723        """Adds a new payment to the payments history."""
724        if user.ident is not None :
725            self.doModify("INSERT INTO payments (userid, amount, description) VALUES (%s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(amount), self.doQuote(unicodeToDatabase(comment))))
726        else :
727            self.doModify("INSERT INTO payments (userid, amount, description) VALUES ((SELECT id FROM users WHERE username=%s), %s, %s)" % (self.doQuote(unicodeToDatabase(user.Name)), self.doQuote(amount), self.doQuote(unicodeToDatabase(comment))))
728
729    def writeLastJobSize(self, lastjob, jobsize, jobprice) :
730        """Sets the last job's size permanently."""
731        self.doModify("UPDATE jobhistory SET jobsize=%s, jobprice=%s WHERE id=%s" % (self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(lastjob.ident)))
732
733    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) :
734        """Adds a job in a printer's history."""
735        if self.privacy :
736            # For legal reasons, we want to hide the title, filename and options
737            title = filename = options = "hidden"
738        if (not self.disablehistory) or (not printer.LastJob.Exists) :
739            if jobsize is not None :
740                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)" \
741                                  % (self.doQuote(user.ident),
742                                     self.doQuote(printer.ident),
743                                     self.doQuote(unicodeToDatabase(jobid)),
744                                     self.doQuote(pagecounter),
745                                     self.doQuote(action),
746                                     self.doQuote(jobsize),
747                                     self.doQuote(jobprice),
748                                     self.doQuote(unicodeToDatabase(filename)),
749                                     self.doQuote(unicodeToDatabase(title)),
750                                     self.doQuote(copies),
751                                     self.doQuote(unicodeToDatabase(options)),
752                                     self.doQuote(unicodeToDatabase(clienthost)),
753                                     self.doQuote(jobsizebytes),
754                                     self.doQuote(jobmd5sum),
755                                     self.doQuote(jobpages),
756                                     self.doQuote(unicodeToDatabase(jobbilling)),
757                                     self.doQuote(precomputedsize),
758                                     self.doQuote(precomputedprice)))
759            else :
760                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)" \
761                                  % (self.doQuote(user.ident),
762                                     self.doQuote(printer.ident),
763                                     self.doQuote(unicodeToDatabase(jobid)),
764                                     self.doQuote(pagecounter),
765                                     self.doQuote(action),
766                                     self.doQuote(unicodeToDatabase(filename)),
767                                     self.doQuote(unicodeToDatabase(title)),
768                                     self.doQuote(copies),
769                                     self.doQuote(unicodeToDatabase(options)),
770                                     self.doQuote(unicodeToDatabase(clienthost)),
771                                     self.doQuote(jobsizebytes),
772                                     self.doQuote(jobmd5sum),
773                                     self.doQuote(jobpages),
774                                     self.doQuote(unicodeToDatabase(jobbilling)),
775                                     self.doQuote(precomputedsize),
776                                     self.doQuote(precomputedprice)))
777        else :
778            # here we explicitly want to reset jobsize to NULL if needed
779            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" \
780                              % (self.doQuote(user.ident),
781                                 self.doQuote(unicodeToDatabase(jobid)),
782                                 self.doQuote(pagecounter),
783                                 self.doQuote(action),
784                                 self.doQuote(jobsize),
785                                 self.doQuote(jobprice),
786                                 self.doQuote(unicodeToDatabase(filename)),
787                                 self.doQuote(unicodeToDatabase(title)),
788                                 self.doQuote(copies),
789                                 self.doQuote(unicodeToDatabase(options)),
790                                 self.doQuote(unicodeToDatabase(clienthost)),
791                                 self.doQuote(jobsizebytes),
792                                 self.doQuote(jobmd5sum),
793                                 self.doQuote(jobpages),
794                                 self.doQuote(unicodeToDatabase(jobbilling)),
795                                 self.doQuote(precomputedsize),
796                                 self.doQuote(precomputedprice),
797                                 self.doQuote(printer.LastJob.ident)))
798
799    def saveUserPQuota(self, userpquota) :
800        """Saves an user print quota entry."""
801        self.doModify("UPDATE userpquota SET softlimit=%s, hardlimit=%s, warncount=%s, datelimit=%s, pagecounter=%s, lifepagecounter=%s, maxjobsize=%s WHERE id=%s" \
802                              % (self.doQuote(userpquota.SoftLimit),
803                                 self.doQuote(userpquota.HardLimit),
804                                 self.doQuote(userpquota.WarnCount or 0),
805                                 self.doQuote(userpquota.DateLimit),
806                                 self.doQuote(userpquota.PageCounter or 0),
807                                 self.doQuote(userpquota.LifePageCounter or 0),
808                                 self.doQuote(userpquota.MaxJobSize),
809                                 self.doQuote(userpquota.ident)))
810
811    def writeUserPQuotaWarnCount(self, userpquota, warncount) :
812        """Sets the warn counter value for a user quota."""
813        self.doModify("UPDATE userpquota SET warncount=%s WHERE id=%s" \
814                          % (self.doQuote(warncount),
815                             self.doQuote(userpquota.ident)))
816
817    def increaseUserPQuotaWarnCount(self, userpquota) :
818        """Increases the warn counter value for a user quota."""
819        self.doModify("UPDATE userpquota SET warncount=warncount+1 WHERE id=%s" \
820                          % self.doQuote(userpquota.ident))
821
822    def saveGroupPQuota(self, grouppquota) :
823        """Saves a group print quota entry."""
824        self.doModify("UPDATE grouppquota SET softlimit=%s, hardlimit=%s, datelimit=%s WHERE id=%s" \
825                              % (self.doQuote(grouppquota.SoftLimit),
826                                 self.doQuote(grouppquota.HardLimit),
827                                 self.doQuote(grouppquota.DateLimit),
828                                 self.doQuote(grouppquota.ident)))
829
830    def writePrinterToGroup(self, pgroup, printer) :
831        """Puts a printer into a printer group."""
832        children = []
833        result = self.doSearch("SELECT printerid FROM printergroupsmembers WHERE groupid=%s" % self.doQuote(pgroup.ident))
834        if result :
835            for record in result :
836                children.append(record.get("printerid")) # TODO : put this into the database integrity rules
837        if printer.ident not in children :
838            self.doModify("INSERT INTO printergroupsmembers (groupid, printerid) VALUES (%s, %s)" % (self.doQuote(pgroup.ident), self.doQuote(printer.ident)))
839
840    def removePrinterFromGroup(self, pgroup, printer) :
841        """Removes a printer from a printer group."""
842        self.doModify("DELETE FROM printergroupsmembers WHERE groupid=%s AND printerid=%s" % (self.doQuote(pgroup.ident), self.doQuote(printer.ident)))
843
844    def retrieveHistory(self, user=None, printer=None, hostname=None, billingcode=None, jobid=None, limit=100, start=None, end=None) :
845        """Retrieves all print jobs for user on printer (or all) between start and end date, limited to first 100 results."""
846        query = "SELECT jobhistory.*,username,printername FROM jobhistory,users,printers WHERE users.id=userid AND printers.id=printerid"
847        where = []
848        if user is not None : # user.ident is None anyway if user doesn't exist
849            where.append("userid=%s" % self.doQuote(user.ident))
850        if printer is not None : # printer.ident is None anyway if printer doesn't exist
851            where.append("printerid=%s" % self.doQuote(printer.ident))
852        if hostname is not None :
853            where.append("hostname=%s" % self.doQuote(unicodeToDatabase(hostname)))
854        if billingcode is not None :
855            where.append("billingcode=%s" % self.doQuote(unicodeToDatabase(billingcode)))
856        if jobid is not None :
857            where.append("jobid=%s" % self.doQuote(unicodeToDatabase(jobid)))
858        if start is not None :
859            where.append("jobdate>=%s" % self.doQuote(start))
860        if end is not None :
861            where.append("jobdate<=%s" % self.doQuote(end))
862        if where :
863            query += " AND %s" % " AND ".join(where)
864        query += " ORDER BY jobhistory.id DESC"
865        if limit :
866            # TODO : LIMIT is not supported under DB2.
867            # TODO : so we must use something like " FETCH FIRST %s ROWS ONLY" % self.doQuote(int(limit))
868            query += " LIMIT %s" % self.doQuote(int(limit))
869        jobs = []
870        result = self.doSearch(query)
871        if result :
872            for fields in result :
873                job = self.storageJobFromRecord(fields)
874                jobs.append(job)
875        return jobs
876
877    def deleteUser(self, user) :
878        """Completely deletes an user from the database."""
879        # TODO : What should we do if we delete the last person who used a given printer ?
880        # TODO : we can't reassign the last job to the previous one, because next user would be
881        # TODO : incorrectly charged (overcharged).
882        for q in [
883                    "DELETE FROM payments WHERE userid=%s" % self.doQuote(user.ident),
884                    "DELETE FROM groupsmembers WHERE userid=%s" % self.doQuote(user.ident),
885                    "DELETE FROM jobhistory WHERE userid=%s" % self.doQuote(user.ident),
886                    "DELETE FROM userpquota WHERE userid=%s" % self.doQuote(user.ident),
887                    "DELETE FROM users WHERE id=%s" % self.doQuote(user.ident),
888                  ] :
889            self.doModify(q)
890
891    def multipleQueriesInTransaction(self, queries) :
892        """Does many modifications in a single transaction."""
893        self.beginTransaction()
894        try :
895            for q in queries :
896                self.doModify(q)
897        except :
898            self.rollbackTransaction()
899            raise
900        else :
901            self.commitTransaction()
902
903    def deleteManyBillingCodes(self, billingcodes) :
904        """Deletes many billing codes."""
905        codeids = ", ".join(["%s" % self.doQuote(b.ident) for b in billingcodes])
906        if codeids :
907            self.multipleQueriesInTransaction([
908                    "DELETE FROM billingcodes WHERE id IN (%s)" % codeids,])
909
910    def deleteManyUsers(self, users) :
911        """Deletes many users."""
912        userids = ", ".join(["%s" % self.doQuote(u.ident) for u in users])
913        if userids :
914            self.multipleQueriesInTransaction([
915                    "DELETE FROM payments WHERE userid IN (%s)" % userids,
916                    "DELETE FROM groupsmembers WHERE userid IN (%s)" % userids,
917                    "DELETE FROM jobhistory WHERE userid IN (%s)" % userids,
918                    "DELETE FROM userpquota WHERE userid IN (%s)" % userids,
919                    "DELETE FROM users WHERE id IN (%s)" % userids,])
920
921    def deleteManyGroups(self, groups) :
922        """Deletes many groups."""
923        groupids = ", ".join(["%s" % self.doQuote(g.ident) for g in groups])
924        if groupids :
925            self.multipleQueriesInTransaction([
926                    "DELETE FROM groupsmembers WHERE groupid IN (%s)" % groupids,
927                    "DELETE FROM grouppquota WHERE groupid IN (%s)" % groupids,
928                    "DELETE FROM groups WHERE id IN (%s)" % groupids,])
929
930    def deleteManyPrinters(self, printers) :
931        """Deletes many printers."""
932        printerids = ", ".join(["%s" % self.doQuote(p.ident) for p in printers])
933        if printerids :
934            self.multipleQueriesInTransaction([
935                    "DELETE FROM printergroupsmembers WHERE groupid IN (%s) OR printerid IN (%s)" % (printerids, printerids),
936                    "DELETE FROM jobhistory WHERE printerid IN (%s)" % printerids,
937                    "DELETE FROM grouppquota WHERE printerid IN (%s)" % printerids,
938                    "DELETE FROM userpquota WHERE printerid IN (%s)" % printerids,
939                    "DELETE FROM printers WHERE id IN (%s)" % printerids,])
940
941    def deleteManyUserPQuotas(self, printers, users) :
942        """Deletes many user print quota entries."""
943        printerids = ", ".join(["%s" % self.doQuote(p.ident) for p in printers])
944        userids = ", ".join(["%s" % self.doQuote(u.ident) for u in users])
945        if userids and printerids :
946            self.multipleQueriesInTransaction([
947                    "DELETE FROM jobhistory WHERE userid IN (%s) AND printerid IN (%s)" \
948                                 % (userids, printerids),
949                    "DELETE FROM userpquota WHERE userid IN (%s) AND printerid IN (%s)" \
950                                 % (userids, printerids),])
951
952    def deleteManyGroupPQuotas(self, printers, groups) :
953        """Deletes many group print quota entries."""
954        printerids = ", ".join(["%s" % self.doQuote(p.ident) for p in printers])
955        groupids = ", ".join(["%s" % self.doQuote(g.ident) for g in groups])
956        if groupids and printerids :
957            self.multipleQueriesInTransaction([
958                    "DELETE FROM grouppquota WHERE groupid IN (%s) AND printerid IN (%s)" \
959                                 % (groupids, printerids),])
960
961    def deleteUserPQuota(self, upquota) :
962        """Completely deletes an user print quota entry from the database."""
963        for q in [
964                    "DELETE FROM jobhistory WHERE userid=%s AND printerid=%s" \
965                                 % (self.doQuote(upquota.User.ident), self.doQuote(upquota.Printer.ident)),
966                    "DELETE FROM userpquota WHERE id=%s" % self.doQuote(upquota.ident),
967                  ] :
968            self.doModify(q)
969
970    def deleteGroupPQuota(self, gpquota) :
971        """Completely deletes a group print quota entry from the database."""
972        for q in [
973                    "DELETE FROM grouppquota WHERE id=%s" % self.doQuote(gpquota.ident),
974                  ] :
975            self.doModify(q)
976
977    def deleteGroup(self, group) :
978        """Completely deletes a group from the database."""
979        for q in [
980                   "DELETE FROM groupsmembers WHERE groupid=%s" % self.doQuote(group.ident),
981                   "DELETE FROM grouppquota WHERE groupid=%s" % self.doQuote(group.ident),
982                   "DELETE FROM groups WHERE id=%s" % self.doQuote(group.ident),
983                 ] :
984            self.doModify(q)
985
986    def deletePrinter(self, printer) :
987        """Completely deletes a printer from the database."""
988        for q in [
989                    "DELETE FROM printergroupsmembers WHERE groupid=%s OR printerid=%s" % (self.doQuote(printer.ident), self.doQuote(printer.ident)),
990                    "DELETE FROM jobhistory WHERE printerid=%s" % self.doQuote(printer.ident),
991                    "DELETE FROM grouppquota WHERE printerid=%s" % self.doQuote(printer.ident),
992                    "DELETE FROM userpquota WHERE printerid=%s" % self.doQuote(printer.ident),
993                    "DELETE FROM printers WHERE id=%s" % self.doQuote(printer.ident),
994                  ] :
995            self.doModify(q)
996
997    def deleteBillingCode(self, code) :
998        """Completely deletes a billing code from the database."""
999        for q in [
1000                   "DELETE FROM billingcodes WHERE id=%s" % self.doQuote(code.ident),
1001                 ] :
1002            self.doModify(q)
Note: See TracBrowser for help on using the browser.