root / pykota / branches / specialauth / pykota / storage.py @ 3179

Revision 3179, 36.5 kB (checked in by jerome, 17 years ago)

Special DB auth seems to work fine with clear text passwords.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
RevLine 
[695]1# PyKota
[1144]2# -*- coding: ISO-8859-15 -*-
[695]3#
[952]4# PyKota : Print Quotas for CUPS and LPRng
[695]5#
[3133]6# (c) 2003, 2004, 2005, 2006, 2007 Jerome Alet <alet@librelogiciel.com>
[873]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.
[695]11#
[873]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
[2302]19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
[695]20#
21# $Id$
22#
[2066]23#
[695]24
[3056]25"""This module is the database abstraction layer for PyKota."""
26
[2945]27import os
28import imp
[2266]29from mx import DateTime
30
[695]31class PyKotaStorageError(Exception):
[2717]32    """An exception for database related stuff."""
[695]33    def __init__(self, message = ""):
34        self.message = message
35        Exception.__init__(self, message)
36    def __repr__(self):
37        return self.message
38    __str__ = __repr__
[759]39       
[3056]40       
[1041]41class StorageObject :
[2717]42    """Object present in the database."""
[1041]43    def __init__(self, parent) :
44        "Initialize minimal data."""
45        self.parent = parent
46        self.ident = None
[2697]47        self.Description = None
[2676]48        self.isDirty = False
49        self.Exists = False
[1041]50       
[2696]51    def setDescription(self, description=None) :
52        """Sets the object's description."""
53        if description is not None :
54            self.Description = str(description)
55            self.isDirty = True   
[2699]56           
57    def save(self) :       
58        """Saves the object to the database."""
59        if self.isDirty :
60            getattr(self.parent, "save%s" % self.__class__.__name__[7:])(self)
61            self.isDirty = False
[3056]62           
[2696]63       
[1041]64class StorageUser(StorageObject) :       
65    """User class."""
66    def __init__(self, parent, name) :
67        StorageObject.__init__(self, parent)
68        self.Name = name
69        self.LimitBy = None
70        self.AccountBalance = None
71        self.LifeTimePaid = None
[1067]72        self.Email = None
[3179]73        self.Password = None
[2054]74        self.OverCharge = 1.0
[1522]75        self.Payments = [] # TODO : maybe handle this smartly for SQL, for now just don't retrieve them
[2773]76        self.PaymentsBacklog = []
[1041]77       
78    def consumeAccountBalance(self, amount) :     
79        """Consumes an amount of money from the user's account balance."""
[1269]80        self.parent.decreaseUserAccountBalance(self, amount)
81        self.AccountBalance = float(self.AccountBalance or 0.0) - amount
[1041]82       
[2452]83    def setAccountBalance(self, balance, lifetimepaid, comment="") :
[1041]84        """Sets the user's account balance in case he pays more money."""
[1522]85        diff = float(lifetimepaid or 0.0) - float(self.LifeTimePaid or 0.0)
[2707]86        self.AccountBalance = balance
87        self.LifeTimePaid = lifetimepaid
[2773]88        if diff :
89            self.PaymentsBacklog.append((diff, comment))
[2707]90        self.isDirty = True
[1041]91       
[2773]92    def save(self) :   
93        """Saves an user and flush its payments backlog."""
94        for (value, comment) in self.PaymentsBacklog :
95            self.parent.writeNewPayment(self, value, comment)
96        self.PaymentsBacklog = []   
97        StorageObject.save(self)   
98       
[1041]99    def setLimitBy(self, limitby) :   
100        """Sets the user's limiting factor."""
[1062]101        try :
102            limitby = limitby.lower()
103        except AttributeError :   
104            limitby = "quota"
[2452]105        if limitby in ["quota", "balance", \
106                       "noquota", "noprint", "nochange"] :
[1041]107            self.LimitBy = limitby
[2706]108            self.isDirty = True
[1041]109       
[2054]110    def setOverChargeFactor(self, factor) :   
111        """Sets the user's overcharging coefficient."""
112        self.OverCharge = factor
[2706]113        self.isDirty = True
[2054]114       
[2937]115    def setEmail(self, email) :   
116        """Sets the user's email address."""
117        self.Email = email
118        self.isDirty = True
119       
[1041]120    def delete(self) :   
[2717]121        """Deletes an user from the database."""
[2801]122        self.parent.deleteUser(self)
123        self.parent.flushEntry("USERS", self.Name)
124        if self.parent.usecache :
125            for (k, v) in self.parent.caches["USERPQUOTAS"].items() :
126                if v.User.Name == self.Name :
127                    self.parent.flushEntry("USERPQUOTAS", "%s@%s" % (v.User.Name, v.Printer.Name))
128        self.Exists = False
129        self.isDirty = False           
[1041]130       
[3060]131    def refund(self, amount, reason) :
[3056]132        """Refunds a number of credits to an user."""
133        self.consumeAccountBalance(-amount)
[3060]134        self.parent.writeNewPayment(self, -amount, reason)
[3056]135       
136       
[1041]137class StorageGroup(StorageObject) :       
138    """User class."""
139    def __init__(self, parent, name) :
140        StorageObject.__init__(self, parent)
141        self.Name = name
142        self.LimitBy = None
143        self.AccountBalance = None
144        self.LifeTimePaid = None
145       
146    def setLimitBy(self, limitby) :   
147        """Sets the user's limiting factor."""
[1062]148        try :
149            limitby = limitby.lower()
150        except AttributeError :   
151            limitby = "quota"
[2452]152        if limitby in ["quota", "balance", "noquota"] :
[1041]153            self.LimitBy = limitby
[2706]154            self.isDirty = True
[1041]155       
[2706]156    def addUserToGroup(self, user) :
157        """Adds an user to an users group."""
158        self.parent.addUserToGroup(user, self)
159       
160    def delUserFromGroup(self, user) :
161        """Removes an user from an users group."""
162        self.parent.delUserFromGroup(user, self)
163       
[1041]164    def delete(self) :   
[2717]165        """Deletes a group from the database."""
[2801]166        self.parent.deleteGroup(self)
167        self.parent.flushEntry("GROUPS", self.Name)
168        if self.parent.usecache :
169            for (k, v) in self.parent.caches["GROUPPQUOTAS"].items() :
170                if v.Group.Name == self.Name :
171                    self.parent.flushEntry("GROUPPQUOTAS", "%s@%s" % (v.Group.Name, v.Printer.Name))
172        self.Exists = False
173        self.isDirty = False           
[1041]174       
[3056]175       
[1041]176class StoragePrinter(StorageObject) :
177    """Printer class."""
178    def __init__(self, parent, name) :
179        StorageObject.__init__(self, parent)
180        self.Name = name
181        self.PricePerPage = None
182        self.PricePerJob = None
[2455]183        self.MaxJobSize = None
[2459]184        self.PassThrough = None
[1041]185       
[1358]186    def __getattr__(self, name) :   
187        """Delays data retrieval until it's really needed."""
188        if name == "LastJob" : 
189            self.LastJob = self.parent.getPrinterLastJob(self)
[3033]190            self.parent.tool.logdebug("Lazy retrieval of last job for printer %s" % self.Name)
[1358]191            return self.LastJob
[3033]192        elif name == "Coefficients" :   
193            self.Coefficients = self.parent.tool.config.getPrinterCoefficients(self.Name)
194            self.parent.tool.logdebug("Lazy retrieval of coefficients for printer %s : %s" % (self.Name, self.Coefficients))
195            return self.Coefficients
[1358]196        else :
197            raise AttributeError, name
198           
[2455]199    def addJobToHistory(self, jobid, user, 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) :
[1041]200        """Adds a job to the printer's history."""
[2455]201        self.parent.writeJobNew(self, user, jobid, pagecounter, action, jobsize, jobprice, filename, title, copies, options, clienthost, jobsizebytes, jobmd5sum, jobpages, jobbilling, precomputedsize, precomputedprice)
[1041]202        # TODO : update LastJob object ? Probably not needed.
203       
[1258]204    def addPrinterToGroup(self, printer) :   
205        """Adds a printer to a printer group."""
[1259]206        if (printer not in self.parent.getParentPrinters(self)) and (printer.ident != self.ident) :
[1258]207            self.parent.writePrinterToGroup(self, printer)
[1269]208            # TODO : reset cached value for printer parents, or add new parent to cached value
[1332]209           
210    def delPrinterFromGroup(self, printer) :   
211        """Deletes a printer from a printer group."""
[1333]212        self.parent.removePrinterFromGroup(self, printer)
213        # TODO : reset cached value for printer parents, or add new parent to cached value
[1258]214       
[1041]215    def setPrices(self, priceperpage = None, priceperjob = None) :   
216        """Sets the printer's prices."""
217        if priceperpage is None :
[1711]218            priceperpage = self.PricePerPage or 0.0
[1041]219        else :   
220            self.PricePerPage = float(priceperpage)
221        if priceperjob is None :   
[1711]222            priceperjob = self.PricePerJob or 0.0
[1041]223        else :   
224            self.PricePerJob = float(priceperjob)
[2686]225        self.isDirty = True   
[1041]226       
[2686]227    def setPassThrough(self, passthrough) :
228        """Sets the printer's passthrough mode."""
229        self.PassThrough = passthrough
230        self.isDirty = True
231       
232    def setMaxJobSize(self, maxjobsize) :
233        """Sets the printer's maximal job size."""
234        self.MaxJobSize = maxjobsize
235        self.isDirty = True
236       
[1330]237    def delete(self) :   
[2717]238        """Deletes a printer from the database."""
[2801]239        self.parent.deletePrinter(self)
240        self.parent.flushEntry("PRINTERS", self.Name)
241        if self.parent.usecache :
242            for (k, v) in self.parent.caches["USERPQUOTAS"].items() :
243                if v.Printer.Name == self.Name :
244                    self.parent.flushEntry("USERPQUOTAS", "%s@%s" % (v.User.Name, v.Printer.Name))
245            for (k, v) in self.parent.caches["GROUPPQUOTAS"].items() :
246                if v.Printer.Name == self.Name :
247                    self.parent.flushEntry("GROUPPQUOTAS", "%s@%s" % (v.Group.Name, v.Printer.Name))
248        self.Exists = False
249        self.isDirty = False           
[1330]250       
[3056]251       
[1041]252class StorageUserPQuota(StorageObject) :
253    """User Print Quota class."""
254    def __init__(self, parent, user, printer) :
255        StorageObject.__init__(self, parent)
256        self.User = user
257        self.Printer = printer
258        self.PageCounter = None
259        self.LifePageCounter = None
260        self.SoftLimit = None
261        self.HardLimit = None
262        self.DateLimit = None
[2054]263        self.WarnCount = None
[2455]264        self.MaxJobSize = None
[1041]265       
[1358]266    def __getattr__(self, name) :   
267        """Delays data retrieval until it's really needed."""
268        if name == "ParentPrintersUserPQuota" : 
269            self.ParentPrintersUserPQuota = (self.User.Exists and self.Printer.Exists and self.parent.getParentPrintersUserPQuota(self)) or []
270            return self.ParentPrintersUserPQuota
271        else :
272            raise AttributeError, name
273       
[1041]274    def setDateLimit(self, datelimit) :   
275        """Sets the date limit for this quota."""
[3050]276        datelimit = DateTime.ISO.ParseDateTime(str(datelimit)[:19])
[1041]277        date = "%04i-%02i-%02i %02i:%02i:%02i" % (datelimit.year, datelimit.month, datelimit.day, datelimit.hour, datelimit.minute, datelimit.second)
278        self.parent.writeUserPQuotaDateLimit(self, date)
279        self.DateLimit = date
280       
281    def setLimits(self, softlimit, hardlimit) :   
282        """Sets the soft and hard limit for this quota."""
283        self.SoftLimit = softlimit
284        self.HardLimit = hardlimit
[1367]285        self.DateLimit = None
[2054]286        self.WarnCount = 0
[2735]287        self.isDirty = True
[1041]288       
[1967]289    def setUsage(self, used) :
290        """Sets the PageCounter and LifePageCounter to used, or if used is + or - prefixed, changes the values of {Life,}PageCounter by that amount."""
291        vused = int(used)
292        if used.startswith("+") or used.startswith("-") :
[2030]293            self.PageCounter += vused
294            self.LifePageCounter += vused
[1967]295        else :
[2030]296            self.PageCounter = self.LifePageCounter = vused
297        self.DateLimit = None
[2054]298        self.WarnCount = 0
[2735]299        self.isDirty = 1
[1967]300
[2066]301    def incDenyBannerCounter(self) :
302        """Increment the deny banner counter for this user quota."""
[2054]303        self.parent.increaseUserPQuotaWarnCount(self)
304        self.WarnCount = (self.WarnCount or 0) + 1
305       
[2066]306    def resetDenyBannerCounter(self) :
307        """Resets the deny banner counter for this user quota."""
308        self.parent.writeUserPQuotaWarnCount(self, 0)
309        self.WarnCount = 0
310       
[1041]311    def reset(self) :   
312        """Resets page counter to 0."""
313        self.PageCounter = 0
[2030]314        self.DateLimit = None
[2735]315        self.isDirty = True
[1041]316       
[1755]317    def hardreset(self) :   
318        """Resets actual and life time page counters to 0."""
319        self.PageCounter = self.LifePageCounter = 0
[2030]320        self.DateLimit = None
[2735]321        self.isDirty = True
[1755]322       
[3036]323    def computeJobPrice(self, jobsize, inkusage=[]) :   
[1285]324        """Computes the job price as the sum of all parent printers' prices + current printer's ones."""
325        totalprice = 0.0   
326        if jobsize :
[2054]327            if self.User.OverCharge != 0.0 :    # optimization, but TODO : beware of rounding errors
328                for upq in [ self ] + self.ParentPrintersUserPQuota :
[3036]329                    totalprice += float(upq.Printer.PricePerJob or 0.0)
330                    pageprice = float(upq.Printer.PricePerPage or 0.0)
331                    if not inkusage :
332                        totalprice += (jobsize * pageprice)
333                    else :   
334                        for pageindex in range(jobsize) :
335                            try :
336                                usage = inkusage[pageindex]
337                            except IndexError :   
338                                self.parent.tool.logdebug("No ink usage information. Using base cost of %f credits for page %i." % (pageprice, pageindex+1))
339                                totalprice += pageprice
340                            else :   
341                                coefficients = self.Printer.Coefficients
342                                for (ink, value) in usage.items() :
343                                    coefvalue = coefficients.get(ink, 1.0)
344                                    coefprice = (coefvalue * pageprice) / 100.0
345                                    inkprice = coefprice * value
346                                    self.parent.tool.logdebug("Applying coefficient %f for color %s (used at %f%% on page %i) to base cost %f gives %f" % (coefvalue, ink, value, pageindex+1, pageprice, inkprice))
[3101]347                                    totalprice += inkprice
[2054]348        if self.User.OverCharge != 1.0 : # TODO : beware of rounding errors
349            overcharged = totalprice * self.User.OverCharge       
[3036]350            self.parent.tool.logdebug("Overcharging %s by a factor of %s ===> User %s will be charged for %s credits." % (totalprice, self.User.OverCharge, self.User.Name, overcharged))
[2054]351            return overcharged
352        else :   
353            return totalprice
[1285]354           
[3036]355    def increasePagesUsage(self, jobsize, inkusage=[]) :
[1041]356        """Increase the value of used pages and money."""
[3036]357        jobprice = self.computeJobPrice(jobsize, inkusage)
[1285]358        if jobsize :
[2409]359            if jobprice :
360                self.User.consumeAccountBalance(jobprice)
361            for upq in [ self ] + self.ParentPrintersUserPQuota :
362                self.parent.increaseUserPQuotaPagesCounters(upq, jobsize)
363                upq.PageCounter = int(upq.PageCounter or 0) + jobsize
364                upq.LifePageCounter = int(upq.LifePageCounter or 0) + jobsize
[1285]365        return jobprice
[1041]366       
[2717]367    def delete(self) :   
368        """Deletes an user print quota entry from the database."""
[2801]369        self.parent.deleteUserPQuota(self)
370        if self.parent.usecache :
371            self.parent.flushEntry("USERPQUOTAS", "%s@%s" % (self.User.Name, self.Printer.Name))
372        self.Exists = False
373        self.isDirty = False
[2717]374       
[3056]375    def refund(self, nbpages) :   
376        """Refunds a number of pages to an user on a particular printer."""
377        self.parent.increaseUserPQuotaPagesCounters(self, -nbpages)
378        self.PageCounter = int(self.PageCounter or 0) - nbpages
379        self.LifePageCounter = int(self.LifePageCounter or 0) - nbpages
380       
381       
[1041]382class StorageGroupPQuota(StorageObject) :
383    """Group Print Quota class."""
384    def __init__(self, parent, group, printer) :
385        StorageObject.__init__(self, parent)
386        self.Group = group
387        self.Printer = printer
388        self.PageCounter = None
389        self.LifePageCounter = None
390        self.SoftLimit = None
391        self.HardLimit = None
392        self.DateLimit = None
[2455]393        self.MaxJobSize = None
[1041]394       
[1365]395    def __getattr__(self, name) :   
396        """Delays data retrieval until it's really needed."""
397        if name == "ParentPrintersGroupPQuota" : 
398            self.ParentPrintersGroupPQuota = (self.Group.Exists and self.Printer.Exists and self.parent.getParentPrintersGroupPQuota(self)) or []
399            return self.ParentPrintersGroupPQuota
400        else :
401            raise AttributeError, name
402       
[2030]403    def reset(self) :   
404        """Resets page counter to 0."""
[2801]405        for user in self.parent.getGroupMembers(self.Group) :
406            uq = self.parent.getUserPQuota(user, self.Printer)
407            uq.reset()
408            uq.save()
[2030]409        self.PageCounter = 0
410        self.DateLimit = None
[2735]411        self.isDirty = True
[2030]412       
413    def hardreset(self) :   
414        """Resets actual and life time page counters to 0."""
[2801]415        for user in self.parent.getGroupMembers(self.Group) :
416            uq = self.parent.getUserPQuota(user, self.Printer)
417            uq.hardreset()
418            uq.save()
[2030]419        self.PageCounter = self.LifePageCounter = 0
420        self.DateLimit = None
[2735]421        self.isDirty = True
[2030]422       
[1041]423    def setDateLimit(self, datelimit) :   
424        """Sets the date limit for this quota."""
[3050]425        datelimit = DateTime.ISO.ParseDateTime(str(datelimit)[:19])
[2450]426        date = "%04i-%02i-%02i %02i:%02i:%02i" % (datelimit.year, \
427                                                  datelimit.month, \
428                                                  datelimit.day, \
429                                                  datelimit.hour, \
430                                                  datelimit.minute, \
431                                                  datelimit.second)
[1041]432        self.parent.writeGroupPQuotaDateLimit(self, date)
433        self.DateLimit = date
434       
435    def setLimits(self, softlimit, hardlimit) :   
436        """Sets the soft and hard limit for this quota."""
437        self.SoftLimit = softlimit
438        self.HardLimit = hardlimit
[1367]439        self.DateLimit = None
[2735]440        self.isDirty = True
[1041]441       
[2717]442    def delete(self) :   
443        """Deletes a group print quota entry from the database."""
[2801]444        self.parent.deleteGroupPQuota(self)
445        if self.parent.usecache :
446            self.parent.flushEntry("GROUPPQUOTAS", "%s@%s" % (self.Group.Name, self.Printer.Name))
447        self.Exists = False
448        self.isDirty = False
[2717]449       
[3056]450       
[1274]451class StorageJob(StorageObject) :
[1692]452    """Printer's Job class."""
[1274]453    def __init__(self, parent) :
[1041]454        StorageObject.__init__(self, parent)
[1358]455        self.UserName = None
456        self.PrinterName = None
[1041]457        self.JobId = None
458        self.PrinterPageCounter = None
[1520]459        self.JobSizeBytes = None
[1041]460        self.JobSize = None
461        self.JobAction = None
462        self.JobDate = None
[1203]463        self.JobPrice = None
464        self.JobFileName = None
465        self.JobTitle = None
466        self.JobCopies = None
467        self.JobOptions = None
[1502]468        self.JobHostName = None
[2054]469        self.JobMD5Sum = None
470        self.JobPages = None
471        self.JobBillingCode = None
[2455]472        self.PrecomputedJobSize = None
473        self.PrecomputedJobPrice = None
[1041]474       
[1358]475    def __getattr__(self, name) :   
476        """Delays data retrieval until it's really needed."""
477        if name == "User" : 
478            self.User = self.parent.getUser(self.UserName)
479            return self.User
480        elif name == "Printer" :   
481            self.Printer = self.parent.getPrinter(self.PrinterName)
482            return self.Printer
483        else :
484            raise AttributeError, name
[3056]485           
[3060]486    def refund(self, reason) :       
[3056]487        """Refund a particular print job."""
[3057]488        if (not self.JobSize) or (self.JobAction in ("DENY", "CANCEL", "REFUND")) :
[3056]489            return
[3062]490           
491        basereason = _("Refunded %i pages and %.3f credits by %s (%s) on %s") \
492                        % (self.JobSize,
493                           self.JobPrice,
[3064]494                           self.parent.tool.originalUserName,
[3062]495                           os.getlogin(),
496                           str(DateTime.now())[:19])
497        if reason :                                               
498            reason = "%s : %s" % (basereason, reason)
499        else :   
500            reason = basereason
501        self.parent.tool.logdebug("Refunding job %s..." % self.ident)   
[3056]502        self.parent.beginTransaction()
503        try :
504            if self.JobBillingCode :
505                bcode = self.parent.getBillingCode(self.JobBillingCode)
506                bcode.refund(self.JobSize, self.JobPrice)
507               
508            if self.User.Exists :
[3060]509                self.User.refund(self.JobPrice, reason)
[3056]510                if self.Printer.Exists :   
511                    upq = self.parent.getUserPQuota(self.User, self.Printer)   
512                    if upq.Exists :
513                        upq.refund(self.JobSize)
514            self.parent.refundJob(self.ident)
515        except :       
516            self.parent.rollbackTransaction()
[3062]517            self.parent.tool.logdebug("Error while refunding job %s." % self.ident)
[3056]518            raise
519        else :   
520            self.parent.commitTransaction()
[3062]521            self.parent.tool.logdebug("Job %s refunded." % self.ident)
[1358]522       
[3056]523       
[1274]524class StorageLastJob(StorageJob) :
525    """Printer's Last Job class."""
526    def __init__(self, parent, printer) :
527        StorageJob.__init__(self, parent)
[1359]528        self.PrinterName = printer.Name # not needed
[1274]529        self.Printer = printer
530       
[3056]531       
[2340]532class StorageBillingCode(StorageObject) :
533    """Billing code class."""
534    def __init__(self, parent, name) :
535        StorageObject.__init__(self, parent)
536        self.BillingCode = name
537        self.PageCounter = None
538        self.Balance = None
539       
540    def delete(self) :   
541        """Deletes the billing code from the database."""
542        self.parent.deleteBillingCode(self)
[2450]543        self.parent.flushEntry("BILLINGCODES", self.BillingCode)
[2686]544        self.isDirty = False
[2676]545        self.Exists = False
[2340]546       
[2342]547    def reset(self, balance=0.0, pagecounter=0) :   
[2340]548        """Resets the pagecounter and balance for this billing code."""
[2686]549        self.Balance = balance
550        self.PageCounter = pagecounter
551        self.isDirty = True
[2340]552       
553    def consume(self, pages, price) :
554        """Consumes some pages and credits for this billing code."""
555        if pages :
[3056]556            self.parent.consumeBillingCode(self, pages, price)
557            self.PageCounter += pages
558            self.Balance -= price
559           
560    def refund(self, pages, price) :
561        """Refunds a particular billing code."""
562        self.consume(-pages, -price)
[2340]563       
[3056]564       
[1130]565class BaseStorage :
566    def __init__(self, pykotatool) :
[1523]567        """Opens the storage connection."""
[1130]568        self.closed = 1
569        self.tool = pykotatool
570        self.usecache = pykotatool.config.getCaching()
[1148]571        self.disablehistory = pykotatool.config.getDisableHistory()
[1875]572        self.privacy = pykotatool.config.getPrivacy()
573        if self.privacy :
574            pykotatool.logdebug("Jobs' title, filename and options will be hidden because of privacy concerns.")
[1130]575        if self.usecache :
576            self.tool.logdebug("Caching enabled.")
[2450]577            self.caches = { "USERS" : {}, \
578                            "GROUPS" : {}, \
579                            "PRINTERS" : {}, \
580                            "USERPQUOTAS" : {}, \
581                            "GROUPPQUOTAS" : {}, \
582                            "JOBS" : {}, \
583                            "LASTJOBS" : {}, \
584                            "BILLINGCODES" : {} }
[1130]585       
[1240]586    def close(self) :   
587        """Must be overriden in children classes."""
588        raise RuntimeError, "BaseStorage.close() must be overriden !"
589       
[1130]590    def __del__(self) :       
591        """Ensures that the database connection is closed."""
592        self.close()
593       
594    def getFromCache(self, cachetype, key) :
595        """Tries to extract something from the cache."""
596        if self.usecache :
597            entry = self.caches[cachetype].get(key)
598            if entry is not None :
599                self.tool.logdebug("Cache hit (%s->%s)" % (cachetype, key))
600            else :   
601                self.tool.logdebug("Cache miss (%s->%s)" % (cachetype, key))
[1743]602            return entry   
[1130]603           
604    def cacheEntry(self, cachetype, key, value) :       
605        """Puts an entry in the cache."""
[1151]606        if self.usecache and getattr(value, "Exists", 0) :
[1130]607            self.caches[cachetype][key] = value
[1132]608            self.tool.logdebug("Cache store (%s->%s)" % (cachetype, key))
[1130]609           
[2450]610    def flushEntry(self, cachetype, key) :       
611        """Removes an entry from the cache."""
612        if self.usecache :
613            try :
614                del self.caches[cachetype][key]
615            except KeyError :   
616                pass
617            else :   
618                self.tool.logdebug("Cache flush (%s->%s)" % (cachetype, key))
619           
[1130]620    def getUser(self, username) :       
621        """Returns the user from cache."""
622        user = self.getFromCache("USERS", username)
623        if user is None :
624            user = self.getUserFromBackend(username)
625            self.cacheEntry("USERS", username, user)
626        return user   
627       
628    def getGroup(self, groupname) :       
629        """Returns the group from cache."""
630        group = self.getFromCache("GROUPS", groupname)
631        if group is None :
632            group = self.getGroupFromBackend(groupname)
633            self.cacheEntry("GROUPS", groupname, group)
634        return group   
635       
636    def getPrinter(self, printername) :       
637        """Returns the printer from cache."""
638        printer = self.getFromCache("PRINTERS", printername)
639        if printer is None :
640            printer = self.getPrinterFromBackend(printername)
641            self.cacheEntry("PRINTERS", printername, printer)
642        return printer   
643       
644    def getUserPQuota(self, user, printer) :       
645        """Returns the user quota information from cache."""
646        useratprinter = "%s@%s" % (user.Name, printer.Name)
647        upquota = self.getFromCache("USERPQUOTAS", useratprinter)
648        if upquota is None :
649            upquota = self.getUserPQuotaFromBackend(user, printer)
650            self.cacheEntry("USERPQUOTAS", useratprinter, upquota)
651        return upquota   
652       
653    def getGroupPQuota(self, group, printer) :       
654        """Returns the group quota information from cache."""
655        groupatprinter = "%s@%s" % (group.Name, printer.Name)
656        gpquota = self.getFromCache("GROUPPQUOTAS", groupatprinter)
657        if gpquota is None :
658            gpquota = self.getGroupPQuotaFromBackend(group, printer)
659            self.cacheEntry("GROUPPQUOTAS", groupatprinter, gpquota)
660        return gpquota   
661       
662    def getPrinterLastJob(self, printer) :       
663        """Extracts last job information for a given printer from cache."""
664        lastjob = self.getFromCache("LASTJOBS", printer.Name)
665        if lastjob is None :
666            lastjob = self.getPrinterLastJobFromBackend(printer)
667            self.cacheEntry("LASTJOBS", printer.Name, lastjob)
668        return lastjob   
669       
[2342]670    def getBillingCode(self, label) :       
671        """Returns the user from cache."""
672        code = self.getFromCache("BILLINGCODES", label)
673        if code is None :
674            code = self.getBillingCodeFromBackend(label)
675            self.cacheEntry("BILLINGCODES", label, code)
676        return code
677       
[1249]678    def getParentPrinters(self, printer) :   
679        """Extracts parent printers information for a given printer from cache."""
[1252]680        if self.usecache :
681            if not hasattr(printer, "Parents") :
682                self.tool.logdebug("Cache miss (%s->Parents)" % printer.Name)
683                printer.Parents = self.getParentPrintersFromBackend(printer)
684                self.tool.logdebug("Cache store (%s->Parents)" % printer.Name)
685            else :
686                self.tool.logdebug("Cache hit (%s->Parents)" % printer.Name)
687        else :       
688            printer.Parents = self.getParentPrintersFromBackend(printer)
[1364]689        for parent in printer.Parents[:] :   
690            printer.Parents.extend(self.getParentPrinters(parent))
[1368]691        uniquedic = {}   
692        for parent in printer.Parents :
693            uniquedic[parent.Name] = parent
694        printer.Parents = uniquedic.values()   
[1252]695        return printer.Parents
[1249]696       
[1137]697    def getGroupMembers(self, group) :       
698        """Returns the group's members list from in-group cache."""
699        if self.usecache :
700            if not hasattr(group, "Members") :
701                self.tool.logdebug("Cache miss (%s->Members)" % group.Name)
702                group.Members = self.getGroupMembersFromBackend(group)
703                self.tool.logdebug("Cache store (%s->Members)" % group.Name)
704            else :
705                self.tool.logdebug("Cache hit (%s->Members)" % group.Name)
706        else :       
707            group.Members = self.getGroupMembersFromBackend(group)
708        return group.Members   
709       
710    def getUserGroups(self, user) :       
711        """Returns the user's groups list from in-user cache."""
712        if self.usecache :
713            if not hasattr(user, "Groups") :
714                self.tool.logdebug("Cache miss (%s->Groups)" % user.Name)
715                user.Groups = self.getUserGroupsFromBackend(user)
716                self.tool.logdebug("Cache store (%s->Groups)" % user.Name)
717            else :
718                self.tool.logdebug("Cache hit (%s->Groups)" % user.Name)
719        else :       
720            user.Groups = self.getUserGroupsFromBackend(user)
721        return user.Groups   
722       
[1240]723    def getParentPrintersUserPQuota(self, userpquota) :     
[1364]724        """Returns all user print quota on the printer and all its parents recursively."""
[1365]725        upquotas = [ ]
[1240]726        for printer in self.getParentPrinters(userpquota.Printer) :
[1395]727            upq = self.getUserPQuota(userpquota.User, printer)
728            if upq.Exists :
729                upquotas.append(upq)
[1240]730        return upquotas       
731       
[1365]732    def getParentPrintersGroupPQuota(self, grouppquota) :     
733        """Returns all group print quota on the printer and all its parents recursively."""
734        gpquotas = [ ]
735        for printer in self.getParentPrinters(grouppquota.Printer) :
[1395]736            gpq = self.getGroupPQuota(grouppquota.Group, printer)
737            if gpq.Exists :
738                gpquotas.append(gpq)
[1365]739        return gpquotas       
740       
[1790]741    def databaseToUserCharset(self, text) :
742        """Converts from database format (UTF-8) to user's charset."""
[2804]743        return self.tool.UTF8ToUserCharset(text)
[1790]744       
745    def userCharsetToDatabase(self, text) :
746        """Converts from user's charset to database format (UTF-8)."""
[2804]747        return self.tool.userCharsetToUTF8(text)
[1790]748       
[2266]749    def cleanDates(self, startdate, enddate) :   
750        """Clean the dates to create a correct filter."""
[2665]751        if startdate :   
752            startdate = startdate.strip().lower()
753        if enddate :   
754            enddate = enddate.strip().lower()
755        if (not startdate) and (not enddate) :   
[2266]756            return (None, None)
[2268]757           
758        now = DateTime.now()   
759        nameddates = ('yesterday', 'today', 'now', 'tomorrow')
[2665]760        datedict = { "start" : startdate, "end" : enddate }   
[2276]761        for limit in datedict.keys() :
762            dateval = datedict[limit]
[2665]763            if dateval :
764                for name in nameddates :
765                    if dateval.startswith(name) :
766                        try :
767                            offset = int(dateval[len(name):])
768                        except :   
769                            offset = 0
770                        dateval = dateval[:len(name)]   
771                        if limit == "start" :
772                            if dateval == "yesterday" :
773                                dateval = (now - 1 + offset).Format("%Y%m%d000000")
774                            elif dateval == "today" :
775                                dateval = (now + offset).Format("%Y%m%d000000")
776                            elif dateval == "now" :
777                                dateval = (now + offset).Format("%Y%m%d%H%M%S")
778                            else : # tomorrow
779                                dateval = (now + 1 + offset).Format("%Y%m%d000000")
780                        else :
781                            if dateval == "yesterday" :
782                                dateval = (now - 1 + offset).Format("%Y%m%d235959")
783                            elif dateval == "today" :
784                                dateval = (now + offset).Format("%Y%m%d235959")
785                            elif dateval == "now" :
786                                dateval = (now + offset).Format("%Y%m%d%H%M%S")
787                            else : # tomorrow
788                                dateval = (now + 1 + offset).Format("%Y%m%d235959")
789                        break
790                       
791                if not dateval.isdigit() :
792                    dateval = None
793                else :   
794                    lgdateval = len(dateval)
795                    if lgdateval == 4 :
796                        if limit == "start" : 
797                            dateval = "%s0101 00:00:00" % dateval
798                        else : 
799                            dateval = "%s1231 23:59:59" % dateval
800                    elif lgdateval == 6 :
801                        if limit == "start" : 
802                            dateval = "%s01 00:00:00" % dateval
803                        else : 
804                            mxdate = DateTime.ISO.ParseDateTime("%s01 00:00:00" % dateval)
805                            dateval = "%s%02i 23:59:59" % (dateval, mxdate.days_in_month)
806                    elif lgdateval == 8 :
807                        if limit == "start" : 
808                            dateval = "%s 00:00:00" % dateval
809                        else : 
810                            dateval = "%s 23:59:59" % dateval
811                    elif lgdateval == 10 :
812                        if limit == "start" : 
813                            dateval = "%s %s:00:00" % (dateval[:8], dateval[8:])
814                        else : 
815                            dateval = "%s %s:59:59" % (dateval[:8], dateval[8:])
816                    elif lgdateval == 12 :
817                        if limit == "start" : 
818                            dateval = "%s %s:%s:00" % (dateval[:8], dateval[8:10], dateval[10:])
819                        else : 
820                            dateval = "%s %s:%s:59" % (dateval[:8], dateval[8:10], dateval[10:])
821                    elif lgdateval == 14 :       
822                        dateval = "%s %s:%s:%s" % (dateval[:8], dateval[8:10], dateval[10:12], dateval[12:])
823                    else :   
824                        dateval = None
825                    try :   
[3050]826                        DateTime.ISO.ParseDateTime(dateval[:19])
[2276]827                    except :   
[2665]828                        dateval = None
829                datedict[limit] = dateval   
[2276]830        (start, end) = (datedict["start"], datedict["end"])
[2665]831        if start and end and (start > end) :
[2276]832            (start, end) = (end, start)
[2953]833        try :   
834            if len(start) == 17 :
835                start = "%s-%s-%s %s" % (start[0:4], start[4:6], start[6:8], start[9:])
836        except TypeError :       
837            pass
838           
839        try :   
840            if len(end) == 17 :
841                end = "%s-%s-%s %s" % (end[0:4], end[4:6], end[6:8], end[9:])
842        except TypeError :       
843            pass
844           
[2276]845        return (start, end)   
[2266]846       
[1021]847def openConnection(pykotatool) :
[2717]848    """Returns a connection handle to the appropriate database."""
[1021]849    backendinfo = pykotatool.config.getStorageBackend()
[800]850    backend = backendinfo["storagebackend"]
[695]851    try :
[2945]852        storagebackend = imp.load_source("storagebackend", 
853                                         os.path.join(os.path.dirname(__file__),
854                                                      "storages",
855                                                      "%s.py" % backend.lower()))
[695]856    except ImportError :
[773]857        raise PyKotaStorageError, _("Unsupported quota storage backend %s") % backend
[695]858    else :   
[800]859        host = backendinfo["storageserver"]
860        database = backendinfo["storagename"]
[1087]861        admin = backendinfo["storageadmin"] or backendinfo["storageuser"]
862        adminpw = backendinfo["storageadminpw"] or backendinfo["storageuserpw"]
[1240]863        return storagebackend.Storage(pykotatool, host, database, admin, adminpw)
Note: See TracBrowser for help on using the browser.