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

Revision 915, 14.2 kB (checked in by jalet, 21 years ago)

More work done on money print charging.
Minor bugs corrected.
All tools now access to the storage as priviledged users, repykota excepted.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1# PyKota
2#
3# PyKota : Print Quotas for CUPS
4#
5# (c) 2003 Jerome Alet <alet@librelogiciel.com>
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19#
20# $Id$
21#
22# $Log$
23# Revision 1.23  2003/04/15 11:30:57  jalet
24# More work done on money print charging.
25# Minor bugs corrected.
26# All tools now access to the storage as priviledged users, repykota excepted.
27#
28# Revision 1.22  2003/04/10 21:47:20  jalet
29# Job history added. Upgrade script neutralized for now !
30#
31# Revision 1.21  2003/04/08 20:38:08  jalet
32# The last job Id is saved now for each printer, this will probably
33# allow other accounting methods in the future.
34#
35# Revision 1.20  2003/03/29 13:45:27  jalet
36# GPL paragraphs were incorrectly (from memory) copied into the sources.
37# Two README files were added.
38# Upgrade script for PostgreSQL pre 1.01 schema was added.
39#
40# Revision 1.19  2003/02/27 08:41:49  jalet
41# DATETIME is not supported anymore in PostgreSQL 7.3 it seems, but
42# TIMESTAMP is.
43#
44# Revision 1.18  2003/02/10 12:07:31  jalet
45# Now repykota should output the recorded total page number for each printer too.
46#
47# Revision 1.17  2003/02/10 08:41:36  jalet
48# edpykota's --reset command line option resets the limit date too.
49#
50# Revision 1.16  2003/02/08 22:39:46  jalet
51# --reset command line option added
52#
53# Revision 1.15  2003/02/08 22:12:09  jalet
54# Life time counter for users and groups added.
55#
56# Revision 1.14  2003/02/07 22:13:13  jalet
57# Perhaps edpykota is now able to add printers !!! Oh, stupid me !
58#
59# Revision 1.13  2003/02/07 00:08:52  jalet
60# Typos
61#
62# Revision 1.12  2003/02/06 23:20:03  jalet
63# warnpykota doesn't need any user/group name argument, mimicing the
64# warnquota disk quota tool.
65#
66# Revision 1.11  2003/02/06 15:05:13  jalet
67# self was forgotten
68#
69# Revision 1.10  2003/02/06 15:03:11  jalet
70# added a method to set the limit date
71#
72# Revision 1.9  2003/02/06 14:52:35  jalet
73# Forgotten import
74#
75# Revision 1.8  2003/02/06 14:49:04  jalet
76# edpykota should be ok now
77#
78# Revision 1.7  2003/02/06 14:28:59  jalet
79# edpykota should be ok, minus some typos
80#
81# Revision 1.6  2003/02/06 09:19:02  jalet
82# More robust behavior (hopefully) when the user or printer is not managed
83# correctly by the Quota System : e.g. cupsFilter added in ppd file, but
84# printer and/or user not 'yet?' in storage.
85#
86# Revision 1.5  2003/02/05 23:26:22  jalet
87# Incorrect handling of grace delay
88#
89# Revision 1.4  2003/02/05 23:02:10  jalet
90# Typo
91#
92# Revision 1.3  2003/02/05 23:00:12  jalet
93# Forgotten import
94# Bad datetime conversion
95#
96# Revision 1.2  2003/02/05 22:28:38  jalet
97# More robust storage
98#
99# Revision 1.1  2003/02/05 21:28:17  jalet
100# Initial import into CVS
101#
102#
103#
104
105import fnmatch
106
107class SQLStorage :   
108    def getMatchingPrinters(self, printerpattern) :
109        """Returns the list of all printers as tuples (id, name) for printer names which match a certain pattern."""
110        printerslist = []
111        # We 'could' do a SELECT printername FROM printers WHERE printername LIKE ...
112        # but we don't because other storages semantics may be different, so every
113        # storage should use fnmatch to match patterns and be storage agnostic
114        result = self.doQuery("SELECT id, printername FROM printers")
115        result = self.doParseResult(result)
116        if result is not None :
117            for printer in result :
118                if fnmatch.fnmatchcase(printer["printername"], printerpattern) :
119                    printerslist.append((printer["id"], printer["printername"]))
120        return printerslist       
121           
122    def getPrinterId(self, printername) :       
123        """Returns a printerid given a printername."""
124        result = self.doQuery("SELECT id FROM printers WHERE printername=%s" % self.doQuote(printername))
125        try :
126            return self.doParseResult(result)[0]["id"]
127        except TypeError :      # Not found   
128            return
129           
130    def getPrinterPrices(self, printerid) :       
131        """Returns a printer prices per page and per job given a printerid."""
132        result = self.doQuery("SELECT priceperpage, priceperjob FROM printers WHERE id=%s" % self.doQuote(printerid))
133        try :
134            printerprices = self.doParseResult(result)[0]
135            return (printerprices["priceperpage"], printerprices["priceperjob"])
136        except TypeError :      # Not found   
137            return
138           
139    def setPrinterPrices(self, printerid, perpage, perjob) :
140        """Sets prices per job and per page for a given printer."""
141        self.doQuery("UPDATE printers SET priceperpage=%s, priceperjob=%s WHERE id=%s" % (self.doQuote(perpage), self.doQuote(perjob), self.doQuote(printerid)))
142   
143    def getUserId(self, username) :
144        """Returns a userid given a username."""
145        result = self.doQuery("SELECT id FROM users WHERE username=%s" % self.doQuote(username))
146        try :
147            return self.doParseResult(result)[0]["id"]
148        except TypeError :      # Not found
149            return
150           
151    def getGroupId(self, groupname) :
152        """Returns a groupid given a grupname."""
153        result = self.doQuery("SELECT id FROM groups WHERE groupname=%s" % self.doQuote(groupname))
154        try :
155            return self.doParseResult(result)[0]["id"]
156        except TypeError :      # Not found
157            return
158           
159    def getJobHistoryId(self, jobid, userid, printerid) :       
160        """Returns the history line's id given a (jobid, userid, printerid)."""
161        result = self.doQuery("SELECT id FROM jobhistory WHERE jobid=%s AND userid=%s AND printerid=%s" % (self.doQuote(jobid), self.doQuote(userid), self.doQuote(printerid)))
162        try :
163            return self.doParseResult(result)[0]["id"]
164        except TypeError :      # Not found   
165            return
166           
167    def getPrinterUsers(self, printerid) :       
168        """Returns the list of usernames which uses a given printer."""
169        result = self.doQuery("SELECT DISTINCT id, username FROM users WHERE id IN (SELECT userid FROM userpquota WHERE printerid=%s) ORDER BY username" % self.doQuote(printerid))
170        result = self.doParseResult(result)
171        if result is None :
172            return []
173        else :   
174            return [(record["id"], record["username"]) for record in result]
175       
176    def getPrinterGroups(self, printerid) :       
177        """Returns the list of groups which uses a given printer."""
178        result = self.doQuery("SELECT DISTINCT id, groupname FROM groups WHERE id IN (SELECT groupid FROM grouppquota WHERE printerid=%s)" % self.doQuote(printerid))
179        result = self.doParseResult(result)
180        if result is None :
181            return []
182        else :   
183            return [(record["id"], record["groupname"]) for record in result]
184       
185    def addPrinter(self, printername) :       
186        """Adds a printer to the quota storage, returns its id."""
187        self.doQuery("INSERT INTO printers (printername) VALUES (%s)" % self.doQuote(printername))
188        return self.getPrinterId(printername)
189       
190    def addUser(self, username) :       
191        """Adds a user to the quota storage, returns its id."""
192        self.doQuery("INSERT INTO users (username) VALUES (%s)" % self.doQuote(username))
193        return self.getUserId(username)
194       
195    def addGroup(self, groupname) :       
196        """Adds a group to the quota storage, returns its id."""
197        self.doQuery("INSERT INTO groups (groupname) VALUES (%s)" % self.doQuote(groupname))
198        return self.getGroupId(groupname)
199       
200    def addUserPQuota(self, username, printerid) :
201        """Initializes a user print quota on a printer, adds the user to the quota storage if needed."""
202        userid = self.getUserId(username)     
203        if userid is None :   
204            userid = self.addUser(username)
205        self.doQuery("INSERT INTO userpquota (userid, printerid) VALUES (%s, %s)" % (self.doQuote(userid), self.doQuote(printerid)))
206        return (userid, printerid)
207       
208    def addGroupPQuota(self, groupname, printerid) :
209        """Initializes a group print quota on a printer, adds the group to the quota storage if needed."""
210        groupid = self.getGroupId(groupname)     
211        if groupid is None :   
212            groupid = self.addUser(groupname)
213        self.doQuery("INSERT INTO grouppquota (groupid, printerid) VALUES (%s, %s)" % (self.doQuote(groupid), self.doQuote(printerid)))
214        return (groupid, printerid)
215       
216    def increaseUserBalance(self, userid, amount) :   
217        """Increases (or decreases) an user's account balance by a given amount."""
218        self.doQuery("UPDATE users SET balance=balance+(%s), lifetimepaid=lifetimepaid+(%s) WHERE id=%s" % (self.doQuote(amount), self.doQuote(amount), self.doQuote(userid)))
219       
220    def getUserBalance(self, userid) :   
221        """Returns the current account balance for a given user."""
222        result = self.doQuery("SELECT balance FROM users WHERE id=%s" % self.doQuote(userid))
223        try :
224            return self.doParseResult(result)[0]["balance"]
225        except TypeError :      # Not found   
226            return
227       
228    def setUserBalance(self, userid, balance) :   
229        """Sets the account balance for a given user to a fixed value."""
230        current = self.getUserBalance(userid)
231        difference = balance - current
232        self.increaseUserBalance(userid, difference)
233       
234    def limitUserByQuota(self, userid) :   
235        """Limits a given user based on print quota."""
236        self.doQuery("UPDATE users SET limitby='quota' WHERE id=%s" % self.doQuote(userid))
237       
238    def limitUserByBalance(self, userid) :   
239        """Limits a given user based on account balance."""
240        self.doQuery("UPDATE users SET limitby='balance' WHERE id=%s" % self.doQuote(userid))
241       
242    def setUserPQuota(self, userid, printerid, softlimit, hardlimit) :
243        """Sets soft and hard limits for a user quota on a specific printer given (userid, printerid)."""
244        self.doQuery("UPDATE userpquota SET softlimit=%s, hardlimit=%s, datelimit=NULL WHERE userid=%s AND printerid=%s" % (self.doQuote(softlimit), self.doQuote(hardlimit), self.doQuote(userid), self.doQuote(printerid)))
245       
246    def resetUserPQuota(self, userid, printerid) :   
247        """Resets the page counter to zero for a user on a printer. Life time page counter is kept unchanged."""
248        self.doQuery("UPDATE userpquota SET pagecounter=0, datelimit=NULL WHERE userid=%s AND printerid=%s" % (self.doQuote(userid), self.doQuote(printerid)))
249       
250    def updateUserPQuota(self, userid, printerid, pagecount) :
251        """Updates the used user Quota information given (userid, printerid) and a job size in pages."""
252        jobprice = self.computePrinterJobPrice(printerid, pagecount)
253        queries = []   
254        queries.append("UPDATE userpquota SET lifepagecounter=lifepagecounter+(%s), pagecounter=pagecounter+(%s) WHERE userid=%s AND printerid=%s" % (self.doQuote(pagecount), self.doQuote(pagecount), self.doQuote(userid), self.doQuote(printerid)))
255        queries.append("UPDATE users SET balance=balance-(%s) WHERE id=%s" % (self.doQuote(jobprice), self.doQuote(userid)))
256        self.doQuery(queries)
257       
258    def getUserPQuota(self, userid, printerid) :
259        """Returns the Print Quota information for a given (userid, printerid)."""
260        result = self.doQuery("SELECT lifepagecounter, pagecounter, softlimit, hardlimit, datelimit FROM userpquota WHERE userid=%s AND printerid=%s" % (self.doQuote(userid), self.doQuote(printerid)))
261        try :
262            return self.doParseResult(result)[0]
263        except TypeError :      # Not found   
264            return
265       
266    def setUserDateLimit(self, userid, printerid, datelimit) :
267        """Sets the limit date for a soft limit to become an hard one given (userid, printerid)."""
268        self.doQuery("UPDATE userpquota SET datelimit=%s::TIMESTAMP WHERE userid=%s AND printerid=%s" % (self.doQuote("%04i-%02i-%02i %02i:%02i:%02i" % (datelimit.year, datelimit.month, datelimit.day, datelimit.hour, datelimit.minute, datelimit.second)), self.doQuote(userid), self.doQuote(printerid)))
269       
270    def addJobToHistory(self, jobid, userid, printerid, pagecounter, action) :
271        """Adds a job to the history: (jobid, userid, printerid, last page counter taken from requester)."""
272        self.doQuery("INSERT INTO jobhistory (jobid, userid, printerid, pagecounter, action) VALUES (%s, %s, %s, %s, %s)" % (self.doQuote(jobid), self.doQuote(userid), self.doQuote(printerid), self.doQuote(pagecounter), self.doQuote(action)))
273        return self.getJobHistoryId(jobid, userid, printerid) # in case jobid is not sufficient
274   
275    def updateJobSizeInHistory(self, historyid, jobsize) :
276        """Updates a job size in the history given the history line's id."""
277        self.doQuery("UPDATE jobhistory SET jobsize=%s WHERE id=%s" % (self.doQuote(jobsize), self.doQuote(historyid)))
278   
279    def getPrinterPageCounter(self, printerid) :
280        """Returns the last page counter value for a printer given its id, also returns last username, last jobid and history line id."""
281        result = self.doQuery("SELECT jobhistory.id, jobid, userid, username, pagecounter FROM jobhistory, users WHERE printerid=%s AND userid=users.id ORDER BY jobdate DESC LIMIT 1" % self.doQuote(printerid))
282        try :
283            return self.doParseResult(result)[0]
284        except TypeError :      # Not found
285            return
286       
287    def computePrinterJobPrice(self, printerid, jobsize) :   
288        """Returns the price for a job on a given printer."""
289        prices = self.getPrinterPrices(printerid)
290        if prices is None :
291            perpage = perjob = 0.0
292        else :   
293            (perpage, perjob) = prices
294        return perjob + (perpage * jobsize)
Note: See TracBrowser for help on using the browser.