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

Revision 3258, 55.3 kB (checked in by jerome, 16 years ago)

Removed LIMIT where it's not needed (unique key), because LIMIT is
not supported everywhere. Two instances remain, not sure how to
remove them in a portable way without exhausting all the available
memory when the history is big...

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