root / pykota / trunk / pykota / storage.py @ 2665

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

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

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