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

Revision 3165, 55.3 kB (checked in by jerome, 17 years ago)

Added --orderby command line switch to dumpykota.
Doesn't work yet with the LDAP backend, since sorting will have
to be done by PyKota's code instead of relying on the database
backend itself to do the dirty work.

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