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
Line 
1# PyKota
2# -*- coding: ISO-8859-15 -*-
3#
4# PyKota : Print Quotas for CUPS and LPRng
5#
6# (c) 2003, 2004, 2005, 2006, 2007 Jerome Alet <alet@librelogiciel.com>
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20#
21# $Id$
22#
23#
24
25"""This module is the database abstraction layer for PyKota."""
26
27import os
28import imp
29from mx import DateTime
30
31class PyKotaStorageError(Exception):
32    """An exception for database related stuff."""
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__
39       
40       
41class StorageObject :
42    """Object present in the database."""
43    def __init__(self, parent) :
44        "Initialize minimal data."""
45        self.parent = parent
46        self.ident = None
47        self.Description = None
48        self.isDirty = False
49        self.Exists = False
50       
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   
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
62           
63       
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
72        self.Email = None
73        self.Password = None
74        self.OverCharge = 1.0
75        self.Payments = [] # TODO : maybe handle this smartly for SQL, for now just don't retrieve them
76        self.PaymentsBacklog = []
77       
78    def consumeAccountBalance(self, amount) :     
79        """Consumes an amount of money from the user's account balance."""
80        self.parent.decreaseUserAccountBalance(self, amount)
81        self.AccountBalance = float(self.AccountBalance or 0.0) - amount
82       
83    def setAccountBalance(self, balance, lifetimepaid, comment="") :
84        """Sets the user's account balance in case he pays more money."""
85        diff = float(lifetimepaid or 0.0) - float(self.LifeTimePaid or 0.0)
86        self.AccountBalance = balance
87        self.LifeTimePaid = lifetimepaid
88        if diff :
89            self.PaymentsBacklog.append((diff, comment))
90        self.isDirty = True
91       
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       
99    def setLimitBy(self, limitby) :   
100        """Sets the user's limiting factor."""
101        try :
102            limitby = limitby.lower()
103        except AttributeError :   
104            limitby = "quota"
105        if limitby in ["quota", "balance", \
106                       "noquota", "noprint", "nochange"] :
107            self.LimitBy = limitby
108            self.isDirty = True
109       
110    def setOverChargeFactor(self, factor) :   
111        """Sets the user's overcharging coefficient."""
112        self.OverCharge = factor
113        self.isDirty = True
114       
115    def setEmail(self, email) :   
116        """Sets the user's email address."""
117        self.Email = email
118        self.isDirty = True
119       
120    def delete(self) :   
121        """Deletes an user from the database."""
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           
130       
131    def refund(self, amount, reason) :
132        """Refunds a number of credits to an user."""
133        self.consumeAccountBalance(-amount)
134        self.parent.writeNewPayment(self, -amount, reason)
135       
136       
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."""
148        try :
149            limitby = limitby.lower()
150        except AttributeError :   
151            limitby = "quota"
152        if limitby in ["quota", "balance", "noquota"] :
153            self.LimitBy = limitby
154            self.isDirty = True
155       
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       
164    def delete(self) :   
165        """Deletes a group from the database."""
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           
174       
175       
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
183        self.MaxJobSize = None
184        self.PassThrough = None
185       
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)
190            self.parent.tool.logdebug("Lazy retrieval of last job for printer %s" % self.Name)
191            return self.LastJob
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
196        else :
197            raise AttributeError, name
198           
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) :
200        """Adds a job to the printer's history."""
201        self.parent.writeJobNew(self, user, jobid, pagecounter, action, jobsize, jobprice, filename, title, copies, options, clienthost, jobsizebytes, jobmd5sum, jobpages, jobbilling, precomputedsize, precomputedprice)
202        # TODO : update LastJob object ? Probably not needed.
203       
204    def addPrinterToGroup(self, printer) :   
205        """Adds a printer to a printer group."""
206        if (printer not in self.parent.getParentPrinters(self)) and (printer.ident != self.ident) :
207            self.parent.writePrinterToGroup(self, printer)
208            # TODO : reset cached value for printer parents, or add new parent to cached value
209           
210    def delPrinterFromGroup(self, printer) :   
211        """Deletes a printer from a printer group."""
212        self.parent.removePrinterFromGroup(self, printer)
213        # TODO : reset cached value for printer parents, or add new parent to cached value
214       
215    def setPrices(self, priceperpage = None, priceperjob = None) :   
216        """Sets the printer's prices."""
217        if priceperpage is None :
218            priceperpage = self.PricePerPage or 0.0
219        else :   
220            self.PricePerPage = float(priceperpage)
221        if priceperjob is None :   
222            priceperjob = self.PricePerJob or 0.0
223        else :   
224            self.PricePerJob = float(priceperjob)
225        self.isDirty = True   
226       
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       
237    def delete(self) :   
238        """Deletes a printer from the database."""
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           
250       
251       
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
263        self.WarnCount = None
264        self.MaxJobSize = None
265       
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       
274    def setDateLimit(self, datelimit) :   
275        """Sets the date limit for this quota."""
276        datelimit = DateTime.ISO.ParseDateTime(str(datelimit)[:19])
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
285        self.DateLimit = None
286        self.WarnCount = 0
287        self.isDirty = True
288       
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("-") :
293            self.PageCounter += vused
294            self.LifePageCounter += vused
295        else :
296            self.PageCounter = self.LifePageCounter = vused
297        self.DateLimit = None
298        self.WarnCount = 0
299        self.isDirty = 1
300
301    def incDenyBannerCounter(self) :
302        """Increment the deny banner counter for this user quota."""
303        self.parent.increaseUserPQuotaWarnCount(self)
304        self.WarnCount = (self.WarnCount or 0) + 1
305       
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       
311    def reset(self) :   
312        """Resets page counter to 0."""
313        self.PageCounter = 0
314        self.DateLimit = None
315        self.isDirty = True
316       
317    def hardreset(self) :   
318        """Resets actual and life time page counters to 0."""
319        self.PageCounter = self.LifePageCounter = 0
320        self.DateLimit = None
321        self.isDirty = True
322       
323    def computeJobPrice(self, jobsize, inkusage=[]) :   
324        """Computes the job price as the sum of all parent printers' prices + current printer's ones."""
325        totalprice = 0.0   
326        if jobsize :
327            if self.User.OverCharge != 0.0 :    # optimization, but TODO : beware of rounding errors
328                for upq in [ self ] + self.ParentPrintersUserPQuota :
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))
347                                    totalprice += inkprice
348        if self.User.OverCharge != 1.0 : # TODO : beware of rounding errors
349            overcharged = totalprice * self.User.OverCharge       
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))
351            return overcharged
352        else :   
353            return totalprice
354           
355    def increasePagesUsage(self, jobsize, inkusage=[]) :
356        """Increase the value of used pages and money."""
357        jobprice = self.computeJobPrice(jobsize, inkusage)
358        if jobsize :
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
365        return jobprice
366       
367    def delete(self) :   
368        """Deletes an user print quota entry from the database."""
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
374       
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       
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
393        self.MaxJobSize = None
394       
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       
403    def reset(self) :   
404        """Resets page counter to 0."""
405        for user in self.parent.getGroupMembers(self.Group) :
406            uq = self.parent.getUserPQuota(user, self.Printer)
407            uq.reset()
408            uq.save()
409        self.PageCounter = 0
410        self.DateLimit = None
411        self.isDirty = True
412       
413    def hardreset(self) :   
414        """Resets actual and life time page counters to 0."""
415        for user in self.parent.getGroupMembers(self.Group) :
416            uq = self.parent.getUserPQuota(user, self.Printer)
417            uq.hardreset()
418            uq.save()
419        self.PageCounter = self.LifePageCounter = 0
420        self.DateLimit = None
421        self.isDirty = True
422       
423    def setDateLimit(self, datelimit) :   
424        """Sets the date limit for this quota."""
425        datelimit = DateTime.ISO.ParseDateTime(str(datelimit)[:19])
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)
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
439        self.DateLimit = None
440        self.isDirty = True
441       
442    def delete(self) :   
443        """Deletes a group print quota entry from the database."""
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
449       
450       
451class StorageJob(StorageObject) :
452    """Printer's Job class."""
453    def __init__(self, parent) :
454        StorageObject.__init__(self, parent)
455        self.UserName = None
456        self.PrinterName = None
457        self.JobId = None
458        self.PrinterPageCounter = None
459        self.JobSizeBytes = None
460        self.JobSize = None
461        self.JobAction = None
462        self.JobDate = None
463        self.JobPrice = None
464        self.JobFileName = None
465        self.JobTitle = None
466        self.JobCopies = None
467        self.JobOptions = None
468        self.JobHostName = None
469        self.JobMD5Sum = None
470        self.JobPages = None
471        self.JobBillingCode = None
472        self.PrecomputedJobSize = None
473        self.PrecomputedJobPrice = None
474       
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
485           
486    def refund(self, reason) :       
487        """Refund a particular print job."""
488        if (not self.JobSize) or (self.JobAction in ("DENY", "CANCEL", "REFUND")) :
489            return
490           
491        basereason = _("Refunded %i pages and %.3f credits by %s (%s) on %s") \
492                        % (self.JobSize,
493                           self.JobPrice,
494                           self.parent.tool.originalUserName,
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)   
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 :
509                self.User.refund(self.JobPrice, reason)
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()
517            self.parent.tool.logdebug("Error while refunding job %s." % self.ident)
518            raise
519        else :   
520            self.parent.commitTransaction()
521            self.parent.tool.logdebug("Job %s refunded." % self.ident)
522       
523       
524class StorageLastJob(StorageJob) :
525    """Printer's Last Job class."""
526    def __init__(self, parent, printer) :
527        StorageJob.__init__(self, parent)
528        self.PrinterName = printer.Name # not needed
529        self.Printer = printer
530       
531       
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)
543        self.parent.flushEntry("BILLINGCODES", self.BillingCode)
544        self.isDirty = False
545        self.Exists = False
546       
547    def reset(self, balance=0.0, pagecounter=0) :   
548        """Resets the pagecounter and balance for this billing code."""
549        self.Balance = balance
550        self.PageCounter = pagecounter
551        self.isDirty = True
552       
553    def consume(self, pages, price) :
554        """Consumes some pages and credits for this billing code."""
555        if pages :
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)
563       
564       
565class BaseStorage :
566    def __init__(self, pykotatool) :
567        """Opens the storage connection."""
568        self.closed = 1
569        self.tool = pykotatool
570        self.usecache = pykotatool.config.getCaching()
571        self.disablehistory = pykotatool.config.getDisableHistory()
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.")
575        if self.usecache :
576            self.tool.logdebug("Caching enabled.")
577            self.caches = { "USERS" : {}, \
578                            "GROUPS" : {}, \
579                            "PRINTERS" : {}, \
580                            "USERPQUOTAS" : {}, \
581                            "GROUPPQUOTAS" : {}, \
582                            "JOBS" : {}, \
583                            "LASTJOBS" : {}, \
584                            "BILLINGCODES" : {} }
585       
586    def close(self) :   
587        """Must be overriden in children classes."""
588        raise RuntimeError, "BaseStorage.close() must be overriden !"
589       
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))
602            return entry   
603           
604    def cacheEntry(self, cachetype, key, value) :       
605        """Puts an entry in the cache."""
606        if self.usecache and getattr(value, "Exists", 0) :
607            self.caches[cachetype][key] = value
608            self.tool.logdebug("Cache store (%s->%s)" % (cachetype, key))
609           
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           
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       
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       
678    def getParentPrinters(self, printer) :   
679        """Extracts parent printers information for a given printer from cache."""
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)
689        for parent in printer.Parents[:] :   
690            printer.Parents.extend(self.getParentPrinters(parent))
691        uniquedic = {}   
692        for parent in printer.Parents :
693            uniquedic[parent.Name] = parent
694        printer.Parents = uniquedic.values()   
695        return printer.Parents
696       
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       
723    def getParentPrintersUserPQuota(self, userpquota) :     
724        """Returns all user print quota on the printer and all its parents recursively."""
725        upquotas = [ ]
726        for printer in self.getParentPrinters(userpquota.Printer) :
727            upq = self.getUserPQuota(userpquota.User, printer)
728            if upq.Exists :
729                upquotas.append(upq)
730        return upquotas       
731       
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) :
736            gpq = self.getGroupPQuota(grouppquota.Group, printer)
737            if gpq.Exists :
738                gpquotas.append(gpq)
739        return gpquotas       
740       
741    def databaseToUserCharset(self, text) :
742        """Converts from database format (UTF-8) to user's charset."""
743        return self.tool.UTF8ToUserCharset(text)
744       
745    def userCharsetToDatabase(self, text) :
746        """Converts from user's charset to database format (UTF-8)."""
747        return self.tool.userCharsetToUTF8(text)
748       
749    def cleanDates(self, startdate, enddate) :   
750        """Clean the dates to create a correct filter."""
751        if startdate :   
752            startdate = startdate.strip().lower()
753        if enddate :   
754            enddate = enddate.strip().lower()
755        if (not startdate) and (not enddate) :   
756            return (None, None)
757           
758        now = DateTime.now()   
759        nameddates = ('yesterday', 'today', 'now', 'tomorrow')
760        datedict = { "start" : startdate, "end" : enddate }   
761        for limit in datedict.keys() :
762            dateval = datedict[limit]
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 :   
826                        DateTime.ISO.ParseDateTime(dateval[:19])
827                    except :   
828                        dateval = None
829                datedict[limit] = dateval   
830        (start, end) = (datedict["start"], datedict["end"])
831        if start and end and (start > end) :
832            (start, end) = (end, start)
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           
845        return (start, end)   
846       
847def openConnection(pykotatool) :
848    """Returns a connection handle to the appropriate database."""
849    backendinfo = pykotatool.config.getStorageBackend()
850    backend = backendinfo["storagebackend"]
851    try :
852        storagebackend = imp.load_source("storagebackend", 
853                                         os.path.join(os.path.dirname(__file__),
854                                                      "storages",
855                                                      "%s.py" % backend.lower()))
856    except ImportError :
857        raise PyKotaStorageError, _("Unsupported quota storage backend %s") % backend
858    else :   
859        host = backendinfo["storageserver"]
860        database = backendinfo["storagename"]
861        admin = backendinfo["storageadmin"] or backendinfo["storageuser"]
862        adminpw = backendinfo["storageadminpw"] or backendinfo["storageuserpw"]
863        return storagebackend.Storage(pykotatool, host, database, admin, adminpw)
Note: See TracBrowser for help on using the browser.