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

Revision 2665, 46.3 kB (checked in by jerome, 18 years ago)

Ensures that date based filtering works consistently between LDAP and SQL.
Doesn't set an empty date to the other date anymore, because this gave
unexpected results :
dumpykota --data history start=20060101
gave only the history for 20060101 and not the history from
this date to the current date, as probably most people would
have expected. This is now fixed.

  • 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 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 PyKotaStorageError, BaseStorage, StorageObject, \
26                           StorageUser, StorageGroup, StoragePrinter, \
27                           StorageJob, StorageLastJob, StorageUserPQuota, \
28                           StorageGroupPQuota, StorageBillingCode
29
30class SQLStorage :
31    def createFilter(self, only) :   
32        """Returns the appropriate SQL filter."""
33        if only :
34            expressions = []
35            for (k, v) in only.items() :
36                expressions.append("%s=%s" % (k, self.doQuote(self.userCharsetToDatabase(v))))
37            return " AND ".join(expressions)     
38        return ""       
39       
40    def extractPrinters(self, extractonly={}) :
41        """Extracts all printer records."""
42        thefilter = self.createFilter(extractonly)
43        if thefilter :
44            thefilter = "WHERE %s" % thefilter
45        result = self.doRawSearch("SELECT * FROM printers %s ORDER BY id ASC" % thefilter)
46        return self.prepareRawResult(result)
47       
48    def extractUsers(self, extractonly={}) :
49        """Extracts all user records."""
50        thefilter = self.createFilter(extractonly)
51        if thefilter :
52            thefilter = "WHERE %s" % thefilter
53        result = self.doRawSearch("SELECT * FROM users %s ORDER BY id ASC" % thefilter)
54        return self.prepareRawResult(result)
55       
56    def extractBillingcodes(self, extractonly={}) :
57        """Extracts all billing codes records."""
58        thefilter = self.createFilter(extractonly)
59        if thefilter :
60            thefilter = "WHERE %s" % thefilter
61        result = self.doRawSearch("SELECT * FROM billingcodes %s ORDER BY id ASC" % thefilter)
62        return self.prepareRawResult(result)
63       
64    def extractGroups(self, extractonly={}) :
65        """Extracts all group records."""
66        thefilter = self.createFilter(extractonly)
67        if thefilter :
68            thefilter = "WHERE %s" % thefilter
69        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) %s GROUP BY groups.id,groups.groupname,groups.limitby,groups.description ORDER BY groups.id ASC" % thefilter)
70        return self.prepareRawResult(result)
71       
72    def extractPayments(self, extractonly={}) :
73        """Extracts all payment records."""
74        thefilter = self.createFilter(extractonly)
75        if thefilter :
76            thefilter = "AND %s" % thefilter
77        result = self.doRawSearch("SELECT username,payments.* FROM users,payments WHERE users.id=payments.userid %s ORDER BY payments.id ASC" % thefilter)
78        return self.prepareRawResult(result)
79       
80    def extractUpquotas(self, extractonly={}) :
81        """Extracts all userpquota records."""
82        thefilter = self.createFilter(extractonly)
83        if thefilter :
84            thefilter = "AND %s" % thefilter
85        result = self.doRawSearch("SELECT users.username,printers.printername,userpquota.* FROM users,printers,userpquota WHERE users.id=userpquota.userid AND printers.id=userpquota.printerid %s ORDER BY userpquota.id ASC" % thefilter)
86        return self.prepareRawResult(result)
87       
88    def extractGpquotas(self, extractonly={}) :
89        """Extracts all grouppquota records."""
90        thefilter = self.createFilter(extractonly)
91        if thefilter :
92            thefilter = "AND %s" % thefilter
93        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) %s GROUP BY grouppquota.id,grouppquota.groupid,grouppquota.printerid,grouppquota.softlimit,grouppquota.hardlimit,grouppquota.datelimit,grouppquota.maxjobsize,groups.groupname,printers.printername ORDER BY grouppquota.id" % thefilter)
94        return self.prepareRawResult(result)
95       
96    def extractUmembers(self, extractonly={}) :
97        """Extracts all user groups members."""
98        thefilter = self.createFilter(extractonly)
99        if thefilter :
100            thefilter = "AND %s" % thefilter
101        result = self.doRawSearch("SELECT groups.groupname, users.username, groupsmembers.* FROM groups,users,groupsmembers WHERE users.id=groupsmembers.userid AND groups.id=groupsmembers.groupid %s ORDER BY groupsmembers.groupid, groupsmembers.userid ASC" % thefilter)
102        return self.prepareRawResult(result)
103       
104    def extractPmembers(self, extractonly={}) :
105        """Extracts all printer groups members."""
106        for (k, v) in extractonly.items() :
107            if k == "pgroupname" :
108                del extractonly[k]
109                extractonly["p1.printername"] = v
110            elif k == "printername" :
111                del extractonly[k]
112                extractonly["p2.printername"] = v
113        thefilter = self.createFilter(extractonly)
114        if thefilter :
115            thefilter = "AND %s" % thefilter
116        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 %s ORDER BY printergroupsmembers.groupid, printergroupsmembers.printerid ASC" % thefilter)
117        return self.prepareRawResult(result)
118       
119    def extractHistory(self, extractonly={}) :
120        """Extracts all jobhistory records."""
121        startdate = extractonly.get("start")
122        enddate = extractonly.get("end")
123        for limit in ("start", "end") :
124            try :
125                del extractonly[limit]
126            except KeyError :   
127                pass
128        thefilter = self.createFilter(extractonly)
129        if thefilter :
130            thefilter = "AND %s" % thefilter
131        (startdate, enddate) = self.cleanDates(startdate, enddate)
132        if startdate : 
133            thefilter = "%s AND jobdate>=%s" % (thefilter, self.doQuote(startdate))
134        if enddate : 
135            thefilter = "%s AND jobdate<=%s" % (thefilter, self.doQuote(enddate))
136        result = self.doRawSearch("SELECT users.username,printers.printername,jobhistory.* FROM users,printers,jobhistory WHERE users.id=jobhistory.userid AND printers.id=jobhistory.printerid %s ORDER BY jobhistory.id ASC" % thefilter)
137        return self.prepareRawResult(result)
138           
139    def filterNames(self, records, attribute, patterns=None) :
140        """Returns a list of 'attribute' from a list of records.
141       
142           Logs any missing attribute.
143        """   
144        result = []
145        for record in records :
146            attrval = record.get(attribute, [None])
147            if attrval is None :
148                self.tool.printInfo("Object %s has no %s attribute !" % (repr(record), attribute), "error")
149            else :
150                attrval = self.databaseToUserCharset(attrval)
151                if patterns :
152                    if (not isinstance(patterns, type([]))) and (not isinstance(patterns, type(()))) :
153                        patterns = [ patterns ]
154                    if self.tool.matchString(attrval, patterns) :
155                        result.append(attrval)
156                else :   
157                    result.append(attrval)
158        return result   
159               
160    def getAllBillingCodes(self, billingcode=None) :   
161        """Extracts all billing codes or only the billing codes matching the optional parameter."""
162        result = self.doSearch("SELECT billingcode FROM billingcodes")
163        if result :
164            return self.filterNames(result, "billingcode", billingcode)
165        else :   
166            return []
167       
168    def getAllPrintersNames(self, printername=None) :   
169        """Extracts all printer names or only the printers' names matching the optional parameter."""
170        result = self.doSearch("SELECT printername FROM printers")
171        if result :
172            return self.filterNames(result, "printername", printername)
173        else :   
174            return []
175   
176    def getAllUsersNames(self, username=None) :   
177        """Extracts all user names."""
178        result = self.doSearch("SELECT username FROM users")
179        if result :
180            return self.filterNames(result, "username", username)
181        else :   
182            return []
183       
184    def getAllGroupsNames(self, groupname=None) :   
185        """Extracts all group names."""
186        result = self.doSearch("SELECT groupname FROM groups")
187        if result :
188            return self.filterNames(result, "groupname", groupname)
189        else :
190            return []
191       
192    def getUserNbJobsFromHistory(self, user) :
193        """Returns the number of jobs the user has in history."""
194        result = self.doSearch("SELECT COUNT(*) FROM jobhistory WHERE userid=%s" % self.doQuote(user.ident))
195        if result :
196            return result[0]["count"]
197        return 0
198       
199    def getUserFromBackend(self, username) :   
200        """Extracts user information given its name."""
201        user = StorageUser(self, username)
202        username = self.userCharsetToDatabase(username)
203        result = self.doSearch("SELECT * FROM users WHERE username=%s LIMIT 1" % self.doQuote(username))
204        if result :
205            fields = result[0]
206            user.ident = fields.get("id")
207            user.LimitBy = fields.get("limitby") or "quota"
208            user.AccountBalance = fields.get("balance")
209            user.LifeTimePaid = fields.get("lifetimepaid")
210            user.Email = fields.get("email")
211            user.Description = self.databaseToUserCharset(fields.get("description"))
212            user.OverCharge = fields.get("overcharge", 1.0)
213            user.Exists = 1
214        return user
215       
216    def getGroupFromBackend(self, groupname) :   
217        """Extracts group information given its name."""
218        group = StorageGroup(self, groupname)
219        groupname = self.userCharsetToDatabase(groupname)
220        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" % self.doQuote(groupname))
221        if result :
222            fields = result[0]
223            group.ident = fields.get("id")
224            group.LimitBy = fields.get("limitby") or "quota"
225            group.AccountBalance = fields.get("balance")
226            group.LifeTimePaid = fields.get("lifetimepaid")
227            group.Description = self.databaseToUserCharset(fields.get("description"))
228            group.Exists = 1
229        return group
230       
231    def getPrinterFromBackend(self, printername) :       
232        """Extracts printer information given its name."""
233        printer = StoragePrinter(self, printername)
234        printername = self.userCharsetToDatabase(printername)
235        result = self.doSearch("SELECT * FROM printers WHERE printername=%s LIMIT 1" % self.doQuote(printername))
236        if result :
237            fields = result[0]
238            printer.ident = fields.get("id")
239            printer.PricePerJob = fields.get("priceperjob") or 0.0
240            printer.PricePerPage = fields.get("priceperpage") or 0.0
241            printer.MaxJobSize = fields.get("maxjobsize") or 0
242            printer.PassThrough = fields.get("passthrough") or 0
243            if printer.PassThrough in (1, "1", "t", "true", "TRUE", "True") :
244                printer.PassThrough = 1
245            else :
246                printer.PassThrough = 0
247            printer.Description = self.databaseToUserCharset(fields.get("description") or "")
248            printer.Exists = 1
249        return printer   
250       
251    def getBillingCodeFromBackend(self, label) :       
252        """Extracts a billing code information given its name."""
253        code = StorageBillingCode(self, label)
254        result = self.doSearch("SELECT * FROM billingcodes WHERE billingcode=%s LIMIT 1" % self.doQuote(self.userCharsetToDatabase(label)))
255        if result :
256            fields = result[0]
257            code.ident = fields.get("id")
258            code.Description = self.databaseToUserCharset(fields.get("description") or "")
259            code.Balance = fields.get("balance") or 0.0
260            code.PageCounter = fields.get("pagecounter") or 0
261            code.Exists = 1
262        return code   
263       
264    def getUserPQuotaFromBackend(self, user, printer) :       
265        """Extracts a user print quota."""
266        userpquota = StorageUserPQuota(self, user, printer)
267        if printer.Exists and user.Exists :
268            result = self.doSearch("SELECT * FROM userpquota WHERE userid=%s AND printerid=%s;" % (self.doQuote(user.ident), self.doQuote(printer.ident)))
269            if result :
270                fields = result[0]
271                userpquota.ident = fields.get("id")
272                userpquota.PageCounter = fields.get("pagecounter")
273                userpquota.LifePageCounter = fields.get("lifepagecounter")
274                userpquota.SoftLimit = fields.get("softlimit")
275                userpquota.HardLimit = fields.get("hardlimit")
276                userpquota.DateLimit = fields.get("datelimit")
277                userpquota.WarnCount = fields.get("warncount")
278                userpquota.Exists = 1
279        return userpquota
280       
281    def getGroupPQuotaFromBackend(self, group, printer) :       
282        """Extracts a group print quota."""
283        grouppquota = StorageGroupPQuota(self, group, printer)
284        if group.Exists :
285            result = self.doSearch("SELECT * FROM grouppquota WHERE groupid=%s AND printerid=%s" % (self.doQuote(group.ident), self.doQuote(printer.ident)))
286            if result :
287                fields = result[0]
288                grouppquota.ident = fields.get("id")
289                grouppquota.SoftLimit = fields.get("softlimit")
290                grouppquota.HardLimit = fields.get("hardlimit")
291                grouppquota.DateLimit = fields.get("datelimit")
292                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)" % (self.doQuote(printer.ident), self.doQuote(group.ident)))
293                if result :
294                    fields = result[0]
295                    grouppquota.PageCounter = fields.get("pagecounter") or 0
296                    grouppquota.LifePageCounter = fields.get("lifepagecounter") or 0
297                grouppquota.Exists = 1
298        return grouppquota
299       
300    def getPrinterLastJobFromBackend(self, printer) :       
301        """Extracts a printer's last job information."""
302        lastjob = StorageLastJob(self, printer)
303        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))
304        if result :
305            fields = result[0]
306            lastjob.ident = fields.get("id")
307            lastjob.JobId = fields.get("jobid")
308            lastjob.UserName = self.databaseToUserCharset(fields.get("username"))
309            lastjob.PrinterPageCounter = fields.get("pagecounter")
310            lastjob.JobSize = fields.get("jobsize")
311            lastjob.JobPrice = fields.get("jobprice")
312            lastjob.JobAction = fields.get("action")
313            lastjob.JobFileName = self.databaseToUserCharset(fields.get("filename") or "") 
314            lastjob.JobTitle = self.databaseToUserCharset(fields.get("title") or "") 
315            lastjob.JobCopies = fields.get("copies")
316            lastjob.JobOptions = self.databaseToUserCharset(fields.get("options") or "") 
317            lastjob.JobDate = fields.get("jobdate")
318            lastjob.JobHostName = fields.get("hostname")
319            lastjob.JobSizeBytes = fields.get("jobsizebytes")
320            lastjob.JobMD5Sum = fields.get("md5sum")
321            lastjob.JobPages = fields.get("pages")
322            lastjob.JobBillingCode = self.databaseToUserCharset(fields.get("billingcode"))
323            lastjob.PrecomputedJobSize = fields.get("precomputedjobsize")
324            lastjob.PrecomputedJobPrice = fields.get("precomputedjobprice")
325            if lastjob.JobTitle == lastjob.JobFileName == lastjob.JobOptions == "hidden" :
326                (lastjob.JobTitle, lastjob.JobFileName, lastjob.JobOptions) = (_("Hidden because of privacy concerns"),) * 3
327            lastjob.Exists = 1
328        return lastjob
329           
330    def getGroupMembersFromBackend(self, group) :       
331        """Returns the group's members list."""
332        groupmembers = []
333        result = self.doSearch("SELECT * FROM groupsmembers JOIN users ON groupsmembers.userid=users.id WHERE groupid=%s" % self.doQuote(group.ident))
334        if result :
335            for record in result :
336                user = StorageUser(self, self.databaseToUserCharset(record.get("username")))
337                user.ident = record.get("userid")
338                user.LimitBy = record.get("limitby") or "quota"
339                user.AccountBalance = record.get("balance")
340                user.LifeTimePaid = record.get("lifetimepaid")
341                user.Email = record.get("email")
342                user.OverCharge = record.get("overcharge")
343                user.Exists = 1
344                groupmembers.append(user)
345                self.cacheEntry("USERS", user.Name, user)
346        return groupmembers       
347       
348    def getUserGroupsFromBackend(self, user) :       
349        """Returns the user's groups list."""
350        groups = []
351        result = self.doSearch("SELECT groupname FROM groupsmembers JOIN groups ON groupsmembers.groupid=groups.id WHERE userid=%s" % self.doQuote(user.ident))
352        if result :
353            for record in result :
354                groups.append(self.getGroup(self.databaseToUserCharset(record.get("groupname"))))
355        return groups       
356       
357    def getParentPrintersFromBackend(self, printer) :   
358        """Get all the printer groups this printer is a member of."""
359        pgroups = []
360        result = self.doSearch("SELECT groupid,printername FROM printergroupsmembers JOIN printers ON groupid=id WHERE printerid=%s" % self.doQuote(printer.ident))
361        if result :
362            for record in result :
363                if record["groupid"] != printer.ident : # in case of integrity violation
364                    parentprinter = self.getPrinter(self.databaseToUserCharset(record.get("printername")))
365                    if parentprinter.Exists :
366                        pgroups.append(parentprinter)
367        return pgroups
368       
369    def getMatchingPrinters(self, printerpattern) :
370        """Returns the list of all printers for which name matches a certain pattern."""
371        printers = []
372        # We 'could' do a SELECT printername FROM printers WHERE printername LIKE ...
373        # but we don't because other storages semantics may be different, so every
374        # storage should use fnmatch to match patterns and be storage agnostic
375        result = self.doSearch("SELECT * FROM printers")
376        if result :
377            patterns = printerpattern.split(",")
378            for record in result :
379                pname = self.databaseToUserCharset(record["printername"])
380                if self.tool.matchString(pname, patterns) :
381                    printer = StoragePrinter(self, pname)
382                    printer.ident = record.get("id")
383                    printer.PricePerJob = record.get("priceperjob") or 0.0
384                    printer.PricePerPage = record.get("priceperpage") or 0.0
385                    printer.Description = self.databaseToUserCharset(record.get("description") or "") 
386                    printer.MaxJobSize = record.get("maxjobsize") or 0
387                    printer.PassThrough = record.get("passthrough") or 0
388                    if printer.PassThrough in (1, "1", "t", "true", "TRUE", "True") :
389                        printer.PassThrough = 1
390                    else :
391                        printer.PassThrough = 0
392                    printer.Exists = 1
393                    printers.append(printer)
394                    self.cacheEntry("PRINTERS", printer.Name, printer)
395        return printers       
396       
397    def getMatchingUsers(self, userpattern) :
398        """Returns the list of all users for which name matches a certain pattern."""
399        users = []
400        # We 'could' do a SELECT username FROM users WHERE username LIKE ...
401        # but we don't because other storages semantics may be different, so every
402        # storage should use fnmatch to match patterns and be storage agnostic
403        result = self.doSearch("SELECT * FROM users")
404        if result :
405            patterns = userpattern.split(",")
406            for record in result :
407                uname = self.databaseToUserCharset(record["username"])
408                if self.tool.matchString(uname, patterns) :
409                    user = StorageUser(self, uname)
410                    user.ident = record.get("id")
411                    user.LimitBy = record.get("limitby") or "quota"
412                    user.AccountBalance = record.get("balance")
413                    user.LifeTimePaid = record.get("lifetimepaid")
414                    user.Email = record.get("email")
415                    user.Description = self.databaseToUserCharset(record.get("description"))
416                    user.OverCharge = record.get("overcharge", 1.0)
417                    user.Exists = 1
418                    users.append(user)
419                    self.cacheEntry("USERS", user.Name, user)
420        return users       
421       
422    def getMatchingGroups(self, grouppattern) :
423        """Returns the list of all groups for which name matches a certain pattern."""
424        groups = []
425        # We 'could' do a SELECT groupname FROM groups WHERE groupname LIKE ...
426        # but we don't because other storages semantics may be different, so every
427        # storage should use fnmatch to match patterns and be storage agnostic
428        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")
429        if result :
430            patterns = grouppattern.split(",")
431            for record in result :
432                gname = self.databaseToUserCharset(record["groupname"])
433                if self.tool.matchString(gname, patterns) :
434                    group = StorageGroup(self, gname)
435                    group.ident = record.get("id")
436                    group.LimitBy = record.get("limitby") or "quota"
437                    group.AccountBalance = record.get("balance")
438                    group.LifeTimePaid = record.get("lifetimepaid")
439                    group.Description = self.databaseToUserCharset(record.get("description"))
440                    group.Exists = 1
441                    groups.append(group)
442                    self.cacheEntry("GROUPS", group.Name, group)
443        return groups       
444       
445    def getMatchingBillingCodes(self, billingcodepattern) :
446        """Returns the list of all billing codes for which the label matches a certain pattern."""
447        codes = []
448        result = self.doSearch("SELECT * FROM billingcodes")
449        if result :
450            patterns = billingcodepattern.split(",")
451            for record in result :
452                bcode = self.databaseToUserCharset(record["billingcode"])
453                if self.tool.matchString(bcode, patterns) :
454                    code = StorageBillingCode(self, bcode)
455                    code.ident = record.get("id")
456                    code.Balance = record.get("balance") or 0.0
457                    code.PageCounter = record.get("pagecounter") or 0
458                    code.Description = self.databaseToUserCharset(record.get("description") or "") 
459                    code.Exists = 1
460                    codes.append(code)
461                    self.cacheEntry("BILLINGCODES", code.BillingCode, code)
462        return codes       
463       
464    def getPrinterUsersAndQuotas(self, printer, names=["*"]) :       
465        """Returns the list of users who uses a given printer, along with their quotas."""
466        usersandquotas = []
467        result = self.doSearch("SELECT users.id as uid,username,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))
468        if result :
469            for record in result :
470                uname = self.databaseToUserCharset(record.get("username"))
471                if self.tool.matchString(uname, names) :
472                    user = StorageUser(self, uname)
473                    user.ident = record.get("uid")
474                    user.LimitBy = record.get("limitby") or "quota"
475                    user.AccountBalance = record.get("balance")
476                    user.LifeTimePaid = record.get("lifetimepaid")
477                    user.Email = record.get("email") 
478                    user.OverCharge = record.get("overcharge")
479                    user.Exists = 1
480                    userpquota = StorageUserPQuota(self, user, printer)
481                    userpquota.ident = record.get("id")
482                    userpquota.PageCounter = record.get("pagecounter")
483                    userpquota.LifePageCounter = record.get("lifepagecounter")
484                    userpquota.SoftLimit = record.get("softlimit")
485                    userpquota.HardLimit = record.get("hardlimit")
486                    userpquota.DateLimit = record.get("datelimit")
487                    userpquota.WarnCount = record.get("warncount")
488                    userpquota.Exists = 1
489                    usersandquotas.append((user, userpquota))
490                    self.cacheEntry("USERS", user.Name, user)
491                    self.cacheEntry("USERPQUOTAS", "%s@%s" % (user.Name, printer.Name), userpquota)
492        return usersandquotas
493               
494    def getPrinterGroupsAndQuotas(self, printer, names=["*"]) :       
495        """Returns the list of groups which uses a given printer, along with their quotas."""
496        groupsandquotas = []
497        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))
498        if result :
499            for record in result :
500                gname = self.databaseToUserCharset(record.get("groupname"))
501                if self.tool.matchString(gname, names) :
502                    group = self.getGroup(gname)
503                    grouppquota = self.getGroupPQuota(group, printer)
504                    groupsandquotas.append((group, grouppquota))
505        return groupsandquotas
506       
507    def addPrinter(self, printername) :       
508        """Adds a printer to the quota storage, returns it."""
509        self.doModify("INSERT INTO printers (printername) VALUES (%s)" % self.doQuote(self.userCharsetToDatabase(printername)))
510        return self.getPrinter(printername)
511       
512    def addBillingCode(self, label) :       
513        """Adds a billing code to the quota storage, returns it."""
514        self.doModify("INSERT INTO billingcodes (billingcode) VALUES (%s)" % self.doQuote(self.userCharsetToDatabase(label)))
515        return self.getBillingCode(label)
516       
517    def addUser(self, user) :       
518        """Adds a user to the quota storage, returns it."""
519        self.doModify("INSERT INTO users (username, limitby, balance, lifetimepaid, email, overcharge) VALUES (%s, %s, %s, %s, %s, %s)" % \
520                                         (self.doQuote(self.userCharsetToDatabase(user.Name)), self.doQuote(user.LimitBy or 'quota'), self.doQuote(user.AccountBalance or 0.0), self.doQuote(user.LifeTimePaid or 0.0), self.doQuote(user.Email), self.doQuote(user.OverCharge)))
521        return self.getUser(user.Name)
522       
523    def addGroup(self, group) :       
524        """Adds a group to the quota storage, returns it."""
525        self.doModify("INSERT INTO groups (groupname, limitby) VALUES (%s, %s)" % \
526                                          (self.doQuote(self.userCharsetToDatabase(group.Name)), self.doQuote(group.LimitBy or "quota")))
527        return self.getGroup(group.Name)
528
529    def addUserToGroup(self, user, group) :   
530        """Adds an user to a group."""
531        result = self.doSearch("SELECT COUNT(*) AS mexists FROM groupsmembers WHERE groupid=%s AND userid=%s" % (self.doQuote(group.ident), self.doQuote(user.ident)))
532        try :
533            mexists = int(result[0].get("mexists"))
534        except (IndexError, TypeError) :   
535            mexists = 0
536        if not mexists :   
537            self.doModify("INSERT INTO groupsmembers (groupid, userid) VALUES (%s, %s)" % (self.doQuote(group.ident), self.doQuote(user.ident)))
538           
539    def addUserPQuota(self, user, printer) :
540        """Initializes a user print quota on a printer."""
541        self.doModify("INSERT INTO userpquota (userid, printerid) VALUES (%s, %s)" % (self.doQuote(user.ident), self.doQuote(printer.ident)))
542        return self.getUserPQuota(user, printer)
543       
544    def addGroupPQuota(self, group, printer) :
545        """Initializes a group print quota on a printer."""
546        self.doModify("INSERT INTO grouppquota (groupid, printerid) VALUES (%s, %s)" % (self.doQuote(group.ident), self.doQuote(printer.ident)))
547        return self.getGroupPQuota(group, printer)
548       
549    def writePrinterPrices(self, printer) :   
550        """Write the printer's prices back into the storage."""
551        self.doModify("UPDATE printers SET priceperpage=%s, priceperjob=%s WHERE id=%s" % (self.doQuote(printer.PricePerPage), self.doQuote(printer.PricePerJob), self.doQuote(printer.ident)))
552       
553    def writePrinterDescription(self, printer) :   
554        """Write the printer's description back into the storage."""
555        description = self.userCharsetToDatabase(printer.Description)
556        self.doModify("UPDATE printers SET description=%s WHERE id=%s" % (self.doQuote(description), self.doQuote(printer.ident)))
557       
558    def setPrinterMaxJobSize(self, printer, maxjobsize) :     
559        """Write the printer's maxjobsize attribute."""
560        self.doModify("UPDATE printers SET maxjobsize=%s WHERE id=%s" % (self.doQuote(maxjobsize), self.doQuote(printer.ident)))
561       
562    def setPrinterPassThroughMode(self, printer, passthrough) :
563        """Write the printer's passthrough attribute."""
564        self.doModify("UPDATE printers SET passthrough=%s WHERE id=%s" % (self.doQuote((passthrough and "t") or "f"), self.doQuote(printer.ident)))
565       
566    def writeUserOverCharge(self, user, factor) :
567        """Sets the user's overcharging coefficient."""
568        self.doModify("UPDATE users SET overcharge=%s WHERE id=%s" % (self.doQuote(factor), self.doQuote(user.ident)))
569       
570    def writeUserLimitBy(self, user, limitby) :   
571        """Sets the user's limiting factor."""
572        self.doModify("UPDATE users SET limitby=%s WHERE id=%s" % (self.doQuote(limitby), self.doQuote(user.ident)))
573       
574    def writeGroupLimitBy(self, group, limitby) :   
575        """Sets the group's limiting factor."""
576        self.doModify("UPDATE groups SET limitby=%s WHERE id=%s" % (self.doQuote(limitby), self.doQuote(group.ident)))
577       
578    def writeUserPQuotaDateLimit(self, userpquota, datelimit) :   
579        """Sets the date limit permanently for a user print quota."""
580        self.doModify("UPDATE userpquota SET datelimit=%s WHERE id=%s" % (self.doQuote(datelimit), self.doQuote(userpquota.ident)))
581           
582    def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) :   
583        """Sets the date limit permanently for a group print quota."""
584        self.doModify("UPDATE grouppquota SET datelimit=%s WHERE id=%s" % (self.doQuote(datelimit), self.doQuote(grouppquota.ident)))
585       
586    def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) :   
587        """Increase page counters for a user print quota."""
588        self.doModify("UPDATE userpquota SET pagecounter=pagecounter + %s,lifepagecounter=lifepagecounter + %s WHERE id=%s" % (self.doQuote(nbpages), self.doQuote(nbpages), self.doQuote(userpquota.ident)))
589       
590    def writeUserPQuotaPagesCounters(self, userpquota, newpagecounter, newlifepagecounter) :   
591        """Sets the new page counters permanently for a user print quota."""
592        self.doModify("UPDATE userpquota SET pagecounter=%s, lifepagecounter=%s, warncount=0, datelimit=NULL WHERE id=%s" % (self.doQuote(newpagecounter), self.doQuote(newlifepagecounter), self.doQuote(userpquota.ident)))
593       
594    def writeBillingCodeDescription(self, code) :
595        """Sets the new description for a billing code."""
596        self.doModify("UPDATE billingcodes SET description=%s WHERE id=%s" % (self.doQuote(self.userCharsetToDatabase(code.Description or "")), self.doQuote(code.ident)))
597       
598    def setBillingCodeValues(self, code, newpagecounter, newbalance) :   
599        """Sets the new page counter and balance for a billing code."""
600        self.doModify("UPDATE billingcodes SET balance=%s, pagecounter=%s WHERE id=%s" % (self.doQuote(newbalance), self.doQuote(newpagecounter), self.doQuote(code.ident)))
601       
602    def consumeBillingCode(self, code, pagecounter, balance) :
603        """Consumes from a billing code."""
604        self.doModify("UPDATE billingcodes SET balance=balance + %s, pagecounter=pagecounter + %s WHERE id=%s" % (self.doQuote(balance), self.doQuote(pagecounter), self.doQuote(code.ident)))
605       
606    def decreaseUserAccountBalance(self, user, amount) :   
607        """Decreases user's account balance from an amount."""
608        self.doModify("UPDATE users SET balance=balance - %s WHERE id=%s" % (self.doQuote(amount), self.doQuote(user.ident)))
609       
610    def writeUserAccountBalance(self, user, newbalance, newlifetimepaid=None) :   
611        """Sets the new account balance and eventually new lifetime paid."""
612        if newlifetimepaid is not None :
613            self.doModify("UPDATE users SET balance=%s, lifetimepaid=%s WHERE id=%s" % (self.doQuote(newbalance), self.doQuote(newlifetimepaid), self.doQuote(user.ident)))
614        else :   
615            self.doModify("UPDATE users SET balance=%s WHERE id=%s" % (self.doQuote(newbalance), self.doQuote(user.ident)))
616           
617    def writeNewPayment(self, user, amount, comment="") :
618        """Adds a new payment to the payments history."""
619        self.doModify("INSERT INTO payments (userid, amount, description) VALUES (%s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(amount), self.doQuote(self.userCharsetToDatabase(comment))))
620       
621    def writeLastJobSize(self, lastjob, jobsize, jobprice) :       
622        """Sets the last job's size permanently."""
623        self.doModify("UPDATE jobhistory SET jobsize=%s, jobprice=%s WHERE id=%s" % (self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(lastjob.ident)))
624       
625    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) :
626        """Adds a job in a printer's history."""
627        if self.privacy :   
628            # For legal reasons, we want to hide the title, filename and options
629            title = filename = options = "hidden"
630        filename = self.userCharsetToDatabase(filename)
631        title = self.userCharsetToDatabase(title)
632        options = self.userCharsetToDatabase(options)
633        jobbilling = self.userCharsetToDatabase(jobbilling)
634        if (not self.disablehistory) or (not printer.LastJob.Exists) :
635            if jobsize is not None :
636                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)))
637            else :   
638                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)))
639        else :       
640            # here we explicitly want to reset jobsize to NULL if needed
641            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)))
642           
643    def writeUserPQuotaLimits(self, userpquota, softlimit, hardlimit) :
644        """Sets soft and hard limits for a user quota."""
645        self.doModify("UPDATE userpquota SET softlimit=%s, hardlimit=%s, warncount=0, datelimit=NULL WHERE id=%s" % (self.doQuote(softlimit), self.doQuote(hardlimit), self.doQuote(userpquota.ident)))
646       
647    def writeUserPQuotaWarnCount(self, userpquota, warncount) :
648        """Sets the warn counter value for a user quota."""
649        self.doModify("UPDATE userpquota SET warncount=%s WHERE id=%s" % (self.doQuote(warncount), self.doQuote(userpquota.ident)))
650       
651    def increaseUserPQuotaWarnCount(self, userpquota) :
652        """Increases the warn counter value for a user quota."""
653        self.doModify("UPDATE userpquota SET warncount=warncount+1 WHERE id=%s" % self.doQuote(userpquota.ident))
654       
655    def writeGroupPQuotaLimits(self, grouppquota, softlimit, hardlimit) :
656        """Sets soft and hard limits for a group quota on a specific printer."""
657        self.doModify("UPDATE grouppquota SET softlimit=%s, hardlimit=%s, datelimit=NULL WHERE id=%s" % (self.doQuote(softlimit), self.doQuote(hardlimit), self.doQuote(grouppquota.ident)))
658
659    def writePrinterToGroup(self, pgroup, printer) :
660        """Puts a printer into a printer group."""
661        children = []
662        result = self.doSearch("SELECT printerid FROM printergroupsmembers WHERE groupid=%s" % self.doQuote(pgroup.ident))
663        if result :
664            for record in result :
665                children.append(record.get("printerid")) # TODO : put this into the database integrity rules
666        if printer.ident not in children :       
667            self.doModify("INSERT INTO printergroupsmembers (groupid, printerid) VALUES (%s, %s)" % (self.doQuote(pgroup.ident), self.doQuote(printer.ident)))
668       
669    def removePrinterFromGroup(self, pgroup, printer) :
670        """Removes a printer from a printer group."""
671        self.doModify("DELETE FROM printergroupsmembers WHERE groupid=%s AND printerid=%s" % (self.doQuote(pgroup.ident), self.doQuote(printer.ident)))
672       
673    def retrieveHistory(self, user=None, printer=None, hostname=None, billingcode=None, limit=100, start=None, end=None) :
674        """Retrieves all print jobs for user on printer (or all) between start and end date, limited to first 100 results."""
675        query = "SELECT jobhistory.*,username,printername FROM jobhistory,users,printers WHERE users.id=userid AND printers.id=printerid"
676        where = []
677        if user is not None : # user.ident is None anyway if user doesn't exist
678            where.append("userid=%s" % self.doQuote(user.ident))
679        if printer is not None : # printer.ident is None anyway if printer doesn't exist
680            where.append("printerid=%s" % self.doQuote(printer.ident))
681        if hostname is not None :   
682            where.append("hostname=%s" % self.doQuote(hostname))
683        if billingcode is not None :   
684            where.append("billingcode=%s" % self.doQuote(self.userCharsetToDatabase(billingcode)))
685        if start is not None :   
686            where.append("jobdate>=%s" % self.doQuote(start))
687        if end is not None :   
688            where.append("jobdate<=%s" % self.doQuote(end))
689        if where :   
690            query += " AND %s" % " AND ".join(where)
691        query += " ORDER BY jobhistory.id DESC"
692        if limit :
693            query += " LIMIT %s" % self.doQuote(int(limit))
694        jobs = []   
695        result = self.doSearch(query)   
696        if result :
697            for fields in result :
698                job = StorageJob(self)
699                job.ident = fields.get("id")
700                job.JobId = fields.get("jobid")
701                job.PrinterPageCounter = fields.get("pagecounter")
702                job.JobSize = fields.get("jobsize")
703                job.JobPrice = fields.get("jobprice")
704                job.JobAction = fields.get("action")
705                job.JobFileName = self.databaseToUserCharset(fields.get("filename") or "") 
706                job.JobTitle = self.databaseToUserCharset(fields.get("title") or "") 
707                job.JobCopies = fields.get("copies")
708                job.JobOptions = self.databaseToUserCharset(fields.get("options") or "") 
709                job.JobDate = fields.get("jobdate")
710                job.JobHostName = fields.get("hostname")
711                job.JobSizeBytes = fields.get("jobsizebytes")
712                job.JobMD5Sum = fields.get("md5sum")
713                job.JobPages = fields.get("pages")
714                job.JobBillingCode = self.databaseToUserCharset(fields.get("billingcode") or "")
715                job.PrecomputedJobSize = fields.get("precomputedjobsize")
716                job.PrecomputedJobPrice = fields.get("precomputedjobprice")
717                job.UserName = self.databaseToUserCharset(fields.get("username"))
718                job.PrinterName = self.databaseToUserCharset(fields.get("printername"))
719                if job.JobTitle == job.JobFileName == job.JobOptions == "hidden" :
720                    (job.JobTitle, job.JobFileName, job.JobOptions) = (_("Hidden because of privacy concerns"),) * 3
721                job.Exists = 1
722                jobs.append(job)
723        return jobs
724       
725    def deleteUser(self, user) :   
726        """Completely deletes an user from the Quota Storage."""
727        # TODO : What should we do if we delete the last person who used a given printer ?
728        # TODO : we can't reassign the last job to the previous one, because next user would be
729        # TODO : incorrectly charged (overcharged).
730        for q in [ 
731                    "DELETE FROM payments WHERE userid=%s" % self.doQuote(user.ident),
732                    "DELETE FROM groupsmembers WHERE userid=%s" % self.doQuote(user.ident),
733                    "DELETE FROM jobhistory WHERE userid=%s" % self.doQuote(user.ident),
734                    "DELETE FROM userpquota WHERE userid=%s" % self.doQuote(user.ident),
735                    "DELETE FROM users WHERE id=%s" % self.doQuote(user.ident),
736                  ] :
737            self.doModify(q)
738       
739    def deleteGroup(self, group) :   
740        """Completely deletes a group from the Quota Storage."""
741        for q in [
742                   "DELETE FROM groupsmembers WHERE groupid=%s" % self.doQuote(group.ident),
743                   "DELETE FROM grouppquota WHERE groupid=%s" % self.doQuote(group.ident),
744                   "DELETE FROM groups WHERE id=%s" % self.doQuote(group.ident),
745                 ] : 
746            self.doModify(q)
747           
748    def deletePrinter(self, printer) :   
749        """Completely deletes a printer from the Quota Storage."""
750        for q in [ 
751                    "DELETE FROM printergroupsmembers WHERE groupid=%s OR printerid=%s" % (self.doQuote(printer.ident), self.doQuote(printer.ident)),
752                    "DELETE FROM jobhistory WHERE printerid=%s" % self.doQuote(printer.ident),
753                    "DELETE FROM grouppquota WHERE printerid=%s" % self.doQuote(printer.ident),
754                    "DELETE FROM userpquota WHERE printerid=%s" % self.doQuote(printer.ident),
755                    "DELETE FROM printers WHERE id=%s" % self.doQuote(printer.ident),
756                  ] :
757            self.doModify(q)
758           
759    def deleteBillingCode(self, code) :   
760        """Completely deletes a billing code from the Quota Storage."""
761        for q in [
762                   "DELETE FROM billingcodes WHERE id=%s" % self.doQuote(code.ident),
763                 ] : 
764            self.doModify(q)
765       
Note: See TracBrowser for help on using the browser.