Changeset 3413 for pykota/trunk/pykota/storages
- Timestamp:
- 09/27/08 22:02:37 (16 years ago)
- Location:
- pykota/trunk/pykota/storages
- Files:
-
- 6 modified
Legend:
- Unmodified
- Added
- Removed
-
pykota/trunk/pykota/storages/__init__.py
r3411 r3413 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. -
pykota/trunk/pykota/storages/ldapstorage.py
r3411 r3413 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 23 23 """This module defines a class to access to an LDAP database backend. 24 24 25 My IANA assigned number, for 26 "Conseil Internet & Logiciels Libres, Jerome Alet" 25 My IANA assigned number, for 26 "Conseil Internet & Logiciels Libres, Jerome Alet" 27 27 is 16868. Use this as a base to extend the LDAP schema. 28 28 """ … … 42 42 StorageJob, StorageLastJob, StorageUserPQuota, \ 43 43 StorageGroupPQuota, StorageBillingCode 44 45 from pykota.utils import * 44 45 from pykota.utils import * 46 46 47 47 try : 48 48 import ldap 49 49 import ldap.modlist 50 except ImportError : 50 except ImportError : 51 51 raise PyKotaStorageError, "This python version (%s) doesn't seem to have the python-ldap module installed correctly." % sys.version.split()[0] 52 else : 52 else : 53 53 try : 54 54 from ldap.cidict import cidict 55 except ImportError : 55 except ImportError : 56 56 import UserDict 57 57 sys.stderr.write("ERROR: PyKota requires a newer version of python-ldap. Workaround activated. Please upgrade python-ldap !\n") 58 58 class cidict(UserDict.UserDict) : 59 59 pass # Fake it all, and don't care for case insensitivity : users who need it will have to upgrade. 60 60 61 61 class Storage(BaseStorage) : 62 62 def __init__(self, pykotatool, host, dbname, user, passwd) : … … 68 68 self.savedpasswd = passwd 69 69 self.secondStageInit() 70 71 def secondStageInit(self) : 70 71 def secondStageInit(self) : 72 72 """Second stage initialisation.""" 73 73 BaseStorage.__init__(self, self.savedtool) … … 77 77 try : 78 78 self.tool.logdebug("Trying to open database (host=%s, dbname=%s, user=%s)..." % (self.savedhost, self.saveddbname, self.saveduser)) 79 self.database = ldap.initialize(self.savedhost) 79 self.database = ldap.initialize(self.savedhost) 80 80 if self.info["ldaptls"] : 81 81 # we want TLS … … 85 85 self.database.simple_bind_s(self.saveduser, self.savedpasswd) 86 86 self.basedn = self.saveddbname 87 except ldap.SERVER_DOWN : 87 except ldap.SERVER_DOWN : 88 88 message = "LDAP backend for PyKota seems to be down !" 89 89 self.tool.printInfo("%s" % message, "error") 90 90 self.tool.printInfo("Trying again in 2 seconds...", "warn") 91 91 time.sleep(2) 92 except ldap.LDAPError : 92 except ldap.LDAPError : 93 93 message = "Unable to connect to LDAP server %s as %s." % (self.savedhost, self.saveduser) 94 94 self.tool.printInfo("%s" % message, "error") 95 95 self.tool.printInfo("Trying again in 2 seconds...", "warn") 96 96 time.sleep(2) 97 else : 97 else : 98 98 self.useldapcache = self.tool.config.getLDAPCache() 99 99 if self.useldapcache : … … 103 103 self.tool.logdebug("Database opened (host=%s, dbname=%s, user=%s)" % (self.savedhost, self.saveddbname, self.saveduser)) 104 104 return # All is fine here. 105 raise PyKotaStorageError, message 106 107 def close(self) : 105 raise PyKotaStorageError, message 106 107 def close(self) : 108 108 """Closes the database connection.""" 109 109 if not self.closed : … … 111 111 self.closed = 1 112 112 self.tool.logdebug("Database closed.") 113 114 def genUUID(self) : 113 114 def genUUID(self) : 115 115 """Generates an unique identifier. 116 116 117 117 TODO : this one is not unique accross several print servers, but should be sufficient for testing. 118 118 """ 119 119 return md5.md5("%s-%s" % (time.time(), random.random())).hexdigest() 120 121 def normalizeFields(self, fields) : 120 121 def normalizeFields(self, fields) : 122 122 """Ensure all items are lists.""" 123 123 for (k, v) in fields.items() : … … 125 125 if not v : 126 126 del fields[k] 127 else : 127 else : 128 128 fields[k] = [ v ] 129 return fields 130 131 def beginTransaction(self) : 129 return fields 130 131 def beginTransaction(self) : 132 132 """Starts a transaction.""" 133 133 self.tool.logdebug("Transaction begins... WARNING : No transactions in LDAP !") 134 135 def commitTransaction(self) : 134 135 def commitTransaction(self) : 136 136 """Commits a transaction.""" 137 137 self.tool.logdebug("Transaction committed. WARNING : No transactions in LDAP !") 138 139 def rollbackTransaction(self) : 138 139 def rollbackTransaction(self) : 140 140 """Rollbacks a transaction.""" 141 141 self.tool.logdebug("Transaction aborted. WARNING : No transaction in LDAP !") 142 142 143 143 def doSearch(self, key, fields=None, base="", scope=ldap.SCOPE_SUBTREE, flushcache=0) : 144 144 """Does an LDAP search query.""" … … 151 151 # retrieve ALL user defined attributes ("*") 152 152 # + the createTimestamp attribute, needed by job history 153 # 153 # 154 154 # This may not work with all LDAP servers 155 # but works at least in OpenLDAP (2.1.25) 155 # but works at least in OpenLDAP (2.1.25) 156 156 # and iPlanet Directory Server (5.1 SP3) 157 fields = ["*", "createTimestamp"] 158 157 fields = ["*", "createTimestamp"] 158 159 159 if self.useldapcache and (not flushcache) and (scope == ldap.SCOPE_BASE) and self.ldapcache.has_key(base) : 160 160 entry = self.ldapcache[base] … … 164 164 self.querydebug("QUERY : Filter : %s, BaseDN : %s, Scope : %s, Attributes : %s" % (key, base, scope, fields)) 165 165 result = self.database.search_s(base, scope, key, fields) 166 except ldap.NO_SUCH_OBJECT, msg : 166 except ldap.NO_SUCH_OBJECT, msg : 167 167 raise PyKotaStorageError, (_("Search base %s doesn't seem to exist. Probable misconfiguration. Please double check /etc/pykota/pykota.conf : %s") % (base, msg)) 168 except ldap.LDAPError, msg : 168 except ldap.LDAPError, msg : 169 169 message = (_("Search for %s(%s) from %s(scope=%s) returned no answer.") % (key, fields, base, scope)) + " : %s" % msg 170 170 self.tool.printInfo("LDAP error : %s" % message, "error") … … 172 172 self.close() 173 173 self.secondStageInit() 174 else : 174 else : 175 175 self.querydebug("QUERY : Result : %s" % result) 176 176 result = [ (dn, cidict(attrs)) for (dn, attrs) in result ] … … 181 181 return result 182 182 raise PyKotaStorageError, message 183 183 184 184 def doAdd(self, dn, fields) : 185 185 """Adds an entry in the LDAP directory.""" … … 192 192 self.querydebug("%s" % entry) 193 193 self.database.add_s(dn, entry) 194 except ldap.ALREADY_EXISTS, msg : 194 except ldap.ALREADY_EXISTS, msg : 195 195 raise PyKotaStorageError, "Entry %s already exists : %s" % (dn, msg) 196 196 except ldap.LDAPError, msg : … … 206 206 return dn 207 207 raise PyKotaStorageError, message 208 208 209 209 def doDelete(self, dn) : 210 210 """Deletes an entry from the LDAP directory.""" … … 214 214 self.querydebug("QUERY : Delete(%s)" % dn) 215 215 self.database.delete_s(dn) 216 except ldap.NO_SUCH_OBJECT : 216 except ldap.NO_SUCH_OBJECT : 217 217 self.tool.printInfo("Entry %s was already missing before we deleted it. This **MAY** be normal." % dn, "info") 218 218 except ldap.LDAPError, msg : … … 222 222 self.close() 223 223 self.secondStageInit() 224 else : 224 else : 225 225 if self.useldapcache : 226 226 try : 227 227 self.querydebug("LDAP cache del %s" % dn) 228 228 del self.ldapcache[dn] 229 except KeyError : 229 except KeyError : 230 230 pass 231 return 231 return 232 232 raise PyKotaStorageError, message 233 233 234 234 def doModify(self, dn, fields, ignoreold=1, flushcache=0) : 235 235 """Modifies an entry in the LDAP directory.""" … … 246 246 if k != "createTimestamp" : 247 247 oldentry[k] = v 248 else : 248 else : 249 249 self.querydebug("LDAP cache miss %s" % dn) 250 250 oldentry = self.doSearch("objectClass=*", base=dn, scope=ldap.SCOPE_BASE)[0][1] 251 else : 251 else : 252 252 oldentry = self.doSearch("objectClass=*", base=dn, scope=ldap.SCOPE_BASE, flushcache=flushcache)[0][1] 253 253 for (k, v) in fields.items() : … … 255 255 try : 256 256 oldvalue = v["convert"](oldentry.get(k, [0])[0]) 257 except ValueError : 257 except ValueError : 258 258 self.querydebug("Error converting %s with %s(%s)" % (oldentry.get(k), k, v)) 259 259 oldvalue = 0 260 260 if v["operator"] == '+' : 261 261 newvalue = oldvalue + v["value"] 262 else : 262 else : 263 263 newvalue = oldvalue - v["value"] 264 264 fields[k] = str(newvalue) … … 288 288 try : 289 289 del cachedentry[mtyp] 290 except KeyError : 290 except KeyError : 291 291 pass 292 292 self.querydebug("LDAP cache update %s => %s" % (dn, cachedentry)) 293 293 return dn 294 294 raise PyKotaStorageError, message 295 295 296 296 def filterNames(self, records, attribute, patterns=None) : 297 297 """Returns a list of 'attribute' from a list of records. 298 298 299 299 Logs any missing attribute. 300 """ 300 """ 301 301 result = [] 302 302 for (dn, record) in records : … … 309 309 if (not isinstance(patterns, type([]))) and (not isinstance(patterns, type(()))) : 310 310 patterns = [ patterns ] 311 if self.tool.matchString(attrval, patterns) : 311 if self.tool.matchString(attrval, patterns) : 312 312 result.append(attrval) 313 else : 313 else : 314 314 result.append(attrval) 315 return result 316 317 def getAllBillingCodes(self, billingcode=None) : 315 return result 316 317 def getAllBillingCodes(self, billingcode=None) : 318 318 """Extracts all billing codes or only the billing codes matching the optional parameter.""" 319 319 ldapfilter = "objectClass=pykotaBilling" … … 321 321 if result : 322 322 return self.filterNames(result, "pykotaBillingCode", billingcode) 323 else : 323 else : 324 324 return [] 325 326 def getAllPrintersNames(self, printername=None) : 325 326 def getAllPrintersNames(self, printername=None) : 327 327 """Extracts all printer names or only the printers' names matching the optional parameter.""" 328 328 ldapfilter = "objectClass=pykotaPrinter" … … 330 330 if result : 331 331 return self.filterNames(result, "pykotaPrinterName", printername) 332 else : 332 else : 333 333 return [] 334 335 def getAllUsersNames(self, username=None) : 334 335 def getAllUsersNames(self, username=None) : 336 336 """Extracts all user names or only the users' names matching the optional parameter.""" 337 337 ldapfilter = "objectClass=pykotaAccount" … … 339 339 if result : 340 340 return self.filterNames(result, "pykotaUserName", username) 341 else : 341 else : 342 342 return [] 343 344 def getAllGroupsNames(self, groupname=None) : 343 344 def getAllGroupsNames(self, groupname=None) : 345 345 """Extracts all group names or only the groups' names matching the optional parameter.""" 346 346 ldapfilter = "objectClass=pykotaGroup" … … 348 348 if result : 349 349 return self.filterNames(result, "pykotaGroupName", groupname) 350 else : 350 else : 351 351 return [] 352 352 353 353 def getUserNbJobsFromHistory(self, user) : 354 354 """Returns the number of jobs the user has in history.""" 355 355 result = self.doSearch("(&(pykotaUserName=%s)(objectClass=pykotaJob))" % unicodeToDatabase(user.Name), None, base=self.info["jobbase"]) 356 356 return len(result) 357 358 def getUserFromBackend(self, username) : 357 358 def getUserFromBackend(self, username) : 359 359 """Extracts user information given its name.""" 360 360 user = StorageUser(self, username) … … 377 377 if user.AccountBalance[0].upper() == "NONE" : 378 378 user.AccountBalance = None 379 else : 379 else : 380 380 user.AccountBalance = float(user.AccountBalance[0]) 381 user.AccountBalance = user.AccountBalance or 0.0 381 user.AccountBalance = user.AccountBalance or 0.0 382 382 user.LifeTimePaid = fields.get("pykotaLifeTimePaid") 383 383 user.OverCharge = float(fields.get("pykotaOverCharge", [1.0])[0]) … … 385 385 if user.LifeTimePaid[0].upper() == "NONE" : 386 386 user.LifeTimePaid = None 387 else : 387 else : 388 388 user.LifeTimePaid = float(user.LifeTimePaid[0]) 389 user.LifeTimePaid = user.LifeTimePaid or 0.0 389 user.LifeTimePaid = user.LifeTimePaid or 0.0 390 390 user.Payments = [] 391 391 for payment in fields.get("pykotaPayments", []) : … … 396 396 (date, amount) = payment.split(" # ") 397 397 description = "" 398 else : 398 else : 399 399 description = databaseToUnicode(base64.decodestring(description)) 400 if amount.endswith(" #") : 400 if amount.endswith(" #") : 401 401 amount = amount[:-2] # TODO : should be catched earlier, the bug is above I think 402 402 user.Payments.append((date, float(amount), description)) 403 403 user.Exists = True 404 404 return user 405 406 def getGroupFromBackend(self, groupname) : 405 406 def getGroupFromBackend(self, groupname) : 407 407 """Extracts group information given its name.""" 408 408 group = StorageGroup(self, groupname) … … 423 423 group.Exists = True 424 424 return group 425 426 def getPrinterFromBackend(self, printername) : 425 426 def getPrinterFromBackend(self, printername) : 427 427 """Extracts printer information given its name : returns first matching printer.""" 428 428 printer = StoragePrinter(self, printername) … … 447 447 printer.PassThrough = 0 448 448 printer.uniqueMember = fields.get("uniqueMember", []) 449 printer.Description = databaseToUnicode(fields.get("description", [""])[0]) 449 printer.Description = databaseToUnicode(fields.get("description", [""])[0]) 450 450 printer.Exists = True 451 return printer 452 453 def getUserPQuotaFromBackend(self, user, printer) : 451 return printer 452 453 def getUserPQuotaFromBackend(self, user, printer) : 454 454 """Extracts a user print quota.""" 455 455 userpquota = StorageUserPQuota(self, user, printer) … … 457 457 if self.info["userquotabase"].lower() == "user" : 458 458 base = user.ident 459 else : 459 else : 460 460 base = self.info["userquotabase"] 461 461 result = self.doSearch("(&(objectClass=pykotaUserPQuota)(pykotaUserName=%s)(pykotaPrinterName=%s))" % \ … … 473 473 if userpquota.SoftLimit[0].upper() == "NONE" : 474 474 userpquota.SoftLimit = None 475 else : 475 else : 476 476 userpquota.SoftLimit = int(userpquota.SoftLimit[0]) 477 477 userpquota.HardLimit = fields.get("pykotaHardLimit") … … 479 479 if userpquota.HardLimit[0].upper() == "NONE" : 480 480 userpquota.HardLimit = None 481 elif userpquota.HardLimit is not None : 481 elif userpquota.HardLimit is not None : 482 482 userpquota.HardLimit = int(userpquota.HardLimit[0]) 483 483 userpquota.DateLimit = fields.get("pykotaDateLimit") 484 484 if userpquota.DateLimit is not None : 485 if userpquota.DateLimit[0].upper() == "NONE" : 485 if userpquota.DateLimit[0].upper() == "NONE" : 486 486 userpquota.DateLimit = None 487 else : 487 else : 488 488 userpquota.DateLimit = userpquota.DateLimit[0] 489 489 userpquota.MaxJobSize = fields.get("pykotaMaxJobSize") … … 491 491 if userpquota.MaxJobSize[0].upper() == "NONE" : 492 492 userpquota.MaxJobSize = None 493 else : 493 else : 494 494 userpquota.MaxJobSize = int(userpquota.MaxJobSize[0]) 495 495 userpquota.Exists = True 496 496 return userpquota 497 498 def getGroupPQuotaFromBackend(self, group, printer) : 497 498 def getGroupPQuotaFromBackend(self, group, printer) : 499 499 """Extracts a group print quota.""" 500 500 grouppquota = StorageGroupPQuota(self, group, printer) … … 502 502 if self.info["groupquotabase"].lower() == "group" : 503 503 base = group.ident 504 else : 504 else : 505 505 base = self.info["groupquotabase"] 506 506 result = self.doSearch("(&(objectClass=pykotaGroupPQuota)(pykotaGroupName=%s)(pykotaPrinterName=%s))" % \ … … 515 515 if grouppquota.SoftLimit[0].upper() == "NONE" : 516 516 grouppquota.SoftLimit = None 517 else : 517 else : 518 518 grouppquota.SoftLimit = int(grouppquota.SoftLimit[0]) 519 519 grouppquota.HardLimit = fields.get("pykotaHardLimit") … … 521 521 if grouppquota.HardLimit[0].upper() == "NONE" : 522 522 grouppquota.HardLimit = None 523 else : 523 else : 524 524 grouppquota.HardLimit = int(grouppquota.HardLimit[0]) 525 525 grouppquota.DateLimit = fields.get("pykotaDateLimit") 526 526 if grouppquota.DateLimit is not None : 527 if grouppquota.DateLimit[0].upper() == "NONE" : 527 if grouppquota.DateLimit[0].upper() == "NONE" : 528 528 grouppquota.DateLimit = None 529 else : 529 else : 530 530 grouppquota.DateLimit = grouppquota.DateLimit[0] 531 531 grouppquota.MaxJobSize = fields.get("pykotaMaxJobSize") … … 533 533 if grouppquota.MaxJobSize[0].upper() == "NONE" : 534 534 grouppquota.MaxJobSize = None 535 else : 535 else : 536 536 grouppquota.MaxJobSize = int(grouppquota.MaxJobSize[0]) 537 537 grouppquota.PageCounter = 0 … … 548 548 ["pykotaPageCounter", "pykotaLifePageCounter"], base=base) 549 549 if result : 550 for userpquota in result : 550 for userpquota in result : 551 551 grouppquota.PageCounter += int(userpquota[1].get("pykotaPageCounter", [0])[0] or 0) 552 552 grouppquota.LifePageCounter += int(userpquota[1].get("pykotaLifePageCounter", [0])[0] or 0) 553 553 grouppquota.Exists = True 554 554 return grouppquota 555 556 def getPrinterLastJobFromBackend(self, printer) : 555 556 def getPrinterLastJobFromBackend(self, printer) : 557 557 """Extracts a printer's last job information.""" 558 558 lastjob = StorageLastJob(self, printer) … … 567 567 result = None 568 568 try : 569 result = self.doSearch("objectClass=pykotaJob", [ "pykotaJobSizeBytes", 570 "pykotaHostName", 571 "pykotaUserName", 572 "pykotaPrinterName", 573 "pykotaJobId", 574 "pykotaPrinterPageCounter", 575 "pykotaJobSize", 576 "pykotaAction", 577 "pykotaJobPrice", 578 "pykotaFileName", 579 "pykotaTitle", 580 "pykotaCopies", 581 "pykotaOptions", 582 "pykotaBillingCode", 583 "pykotaPages", 584 "pykotaMD5Sum", 569 result = self.doSearch("objectClass=pykotaJob", [ "pykotaJobSizeBytes", 570 "pykotaHostName", 571 "pykotaUserName", 572 "pykotaPrinterName", 573 "pykotaJobId", 574 "pykotaPrinterPageCounter", 575 "pykotaJobSize", 576 "pykotaAction", 577 "pykotaJobPrice", 578 "pykotaFileName", 579 "pykotaTitle", 580 "pykotaCopies", 581 "pykotaOptions", 582 "pykotaBillingCode", 583 "pykotaPages", 584 "pykotaMD5Sum", 585 585 "pykotaPrecomputedJobSize", 586 586 "pykotaPrecomputedJobPrice", 587 "createTimestamp" ], 587 "createTimestamp" ], 588 588 base="cn=%s,%s" % (lastjobident, self.info["jobbase"]), scope=ldap.SCOPE_BASE) 589 except PyKotaStorageError : 590 pass # Last job entry exists, but job probably doesn't exist anymore. 589 except PyKotaStorageError : 590 pass # Last job entry exists, but job probably doesn't exist anymore. 591 591 if result : 592 592 fields = result[0][1] … … 597 597 try : 598 598 lastjob.JobSize = int(fields.get("pykotaJobSize", [0])[0]) 599 except ValueError : 599 except ValueError : 600 600 lastjob.JobSize = None 601 try : 601 try : 602 602 lastjob.JobPrice = float(fields.get("pykotaJobPrice", [0.0])[0]) 603 except ValueError : 603 except ValueError : 604 604 lastjob.JobPrice = None 605 605 lastjob.JobAction = databaseToUnicode(fields.get("pykotaAction", [""])[0]) 606 lastjob.JobFileName = databaseToUnicode(fields.get("pykotaFileName", [""])[0]) 607 lastjob.JobTitle = databaseToUnicode(fields.get("pykotaTitle", [""])[0]) 606 lastjob.JobFileName = databaseToUnicode(fields.get("pykotaFileName", [""])[0]) 607 lastjob.JobTitle = databaseToUnicode(fields.get("pykotaTitle", [""])[0]) 608 608 lastjob.JobCopies = int(fields.get("pykotaCopies", [0])[0]) 609 lastjob.JobOptions = databaseToUnicode(fields.get("pykotaOptions", [""])[0]) 609 lastjob.JobOptions = databaseToUnicode(fields.get("pykotaOptions", [""])[0]) 610 610 lastjob.JobHostName = databaseToUnicode(fields.get("pykotaHostName", [""])[0]) 611 611 lastjob.JobSizeBytes = fields.get("pykotaJobSizeBytes", [0L])[0] … … 615 615 try : 616 616 lastjob.PrecomputedJobSize = int(fields.get("pykotaPrecomputedJobSize", [0])[0]) 617 except ValueError : 617 except ValueError : 618 618 lastjob.PrecomputedJobSize = None 619 try : 619 try : 620 620 lastjob.PrecomputedJobPrice = float(fields.get("pykotaPrecomputedJobPrice", [0.0])[0]) 621 except ValueError : 621 except ValueError : 622 622 lastjob.PrecomputedJobPrice = None 623 623 if lastjob.JobTitle == lastjob.JobFileName == lastjob.JobOptions == u"hidden" : … … 628 628 lastjob.Exists = True 629 629 return lastjob 630 631 def getGroupMembersFromBackend(self, group) : 630 631 def getGroupMembersFromBackend(self, group) : 632 632 """Returns the group's members list.""" 633 633 groupmembers = [] … … 640 640 for username in result[0][1].get(self.info["groupmembers"], []) : 641 641 groupmembers.append(self.getUser(databaseToUnicode(username))) 642 return groupmembers 643 644 def getUserGroupsFromBackend(self, user) : 642 return groupmembers 643 644 def getUserGroupsFromBackend(self, user) : 645 645 """Returns the user's groups list.""" 646 646 groups = [] … … 660 660 if group.LimitBy is not None : 661 661 group.LimitBy = databaseToUnicode(group.LimitBy[0]) 662 else : 662 else : 663 663 group.LimitBy = u"quota" 664 664 group.AccountBalance = 0.0 … … 671 671 self.cacheEntry("GROUPS", group.Name, group) 672 672 groups.append(group) 673 return groups 674 675 def getParentPrintersFromBackend(self, printer) : 673 return groups 674 675 def getParentPrintersFromBackend(self, printer) : 676 676 """Get all the printer groups this printer is a member of.""" 677 677 pgroups = [] … … 687 687 pgroups.append(parentprinter) 688 688 return pgroups 689 689 690 690 def getMatchingPrinters(self, printerpattern) : 691 691 """Returns the list of all printers for which name matches a certain pattern.""" … … 699 699 try : 700 700 patdict = {}.fromkeys(patterns) 701 except AttributeError : 701 except AttributeError : 702 702 # Python v2.2 or earlier 703 703 patdict = {} … … 718 718 printer.PassThrough = 0 719 719 printer.uniqueMember = fields.get("uniqueMember", []) 720 printer.Description = databaseToUnicode(fields.get("description", [""])[0]) 720 printer.Description = databaseToUnicode(fields.get("description", [""])[0]) 721 721 printer.Exists = True 722 722 printers.append(printer) 723 723 self.cacheEntry("PRINTERS", printer.Name, printer) 724 return printers 725 724 return printers 725 726 726 def getMatchingUsers(self, userpattern) : 727 727 """Returns the list of all users for which name matches a certain pattern.""" … … 735 735 try : 736 736 patdict = {}.fromkeys(patterns) 737 except AttributeError : 737 except AttributeError : 738 738 # Python v2.2 or earlier 739 739 patdict = {} … … 747 747 user.Email = databaseToUnicode(fields.get(self.info["usermail"], [None])[0]) 748 748 user.LimitBy = databaseToUnicode(fields.get("pykotaLimitBy", ["quota"])[0]) 749 user.Description = databaseToUnicode(fields.get("description", [""])[0]) 749 user.Description = databaseToUnicode(fields.get("description", [""])[0]) 750 750 uname = unicodeToDatabase(username) 751 751 result = self.doSearch("(&(objectClass=pykotaAccountBalance)(|(pykotaUserName=%s)(%s=%s)))" % \ … … 763 763 if user.AccountBalance[0].upper() == "NONE" : 764 764 user.AccountBalance = None 765 else : 765 else : 766 766 user.AccountBalance = float(user.AccountBalance[0]) 767 user.AccountBalance = user.AccountBalance or 0.0 767 user.AccountBalance = user.AccountBalance or 0.0 768 768 user.LifeTimePaid = fields.get("pykotaLifeTimePaid") 769 769 if user.LifeTimePaid is not None : 770 770 if user.LifeTimePaid[0].upper() == "NONE" : 771 771 user.LifeTimePaid = None 772 else : 772 else : 773 773 user.LifeTimePaid = float(user.LifeTimePaid[0]) 774 user.LifeTimePaid = user.LifeTimePaid or 0.0 774 user.LifeTimePaid = user.LifeTimePaid or 0.0 775 775 user.Payments = [] 776 776 for payment in fields.get("pykotaPayments", []) : … … 781 781 (date, amount) = payment.split(" # ") 782 782 description = "" 783 else : 783 else : 784 784 description = databaseToUnicode(base64.decodestring(description)) 785 if amount.endswith(" #") : 785 if amount.endswith(" #") : 786 786 amount = amount[:-2] # TODO : should be catched earlier, the bug is above I think 787 787 user.Payments.append((date, float(amount), description)) … … 789 789 users.append(user) 790 790 self.cacheEntry("USERS", user.Name, user) 791 return users 792 791 return users 792 793 793 def getMatchingGroups(self, grouppattern) : 794 794 """Returns the list of all groups for which name matches a certain pattern.""" … … 802 802 try : 803 803 patdict = {}.fromkeys(patterns) 804 except AttributeError : 804 except AttributeError : 805 805 # Python v2.2 or earlier 806 806 patdict = {} … … 812 812 group = StorageGroup(self, groupname) 813 813 group.ident = groupid 814 group.Name = databaseToUnicode(fields.get("pykotaGroupName", [groupname])[0]) 814 group.Name = databaseToUnicode(fields.get("pykotaGroupName", [groupname])[0]) 815 815 group.LimitBy = databaseToUnicode(fields.get("pykotaLimitBy", ["quota"])[0]) 816 group.Description = databaseToUnicode(fields.get("description", [""])[0]) 816 group.Description = databaseToUnicode(fields.get("description", [""])[0]) 817 817 group.AccountBalance = 0.0 818 818 group.LifeTimePaid = 0.0 … … 825 825 self.cacheEntry("GROUPS", group.Name, group) 826 826 return groups 827 828 def getPrinterUsersAndQuotas(self, printer, names=["*"]) : 827 828 def getPrinterUsersAndQuotas(self, printer, names=["*"]) : 829 829 """Returns the list of users who uses a given printer, along with their quotas.""" 830 830 usersandquotas = [] … … 851 851 if userpquota.SoftLimit[0].upper() == "NONE" : 852 852 userpquota.SoftLimit = None 853 else : 853 else : 854 854 userpquota.SoftLimit = int(userpquota.SoftLimit[0]) 855 855 userpquota.HardLimit = fields.get("pykotaHardLimit") … … 857 857 if userpquota.HardLimit[0].upper() == "NONE" : 858 858 userpquota.HardLimit = None 859 elif userpquota.HardLimit is not None : 859 elif userpquota.HardLimit is not None : 860 860 userpquota.HardLimit = int(userpquota.HardLimit[0]) 861 861 userpquota.DateLimit = fields.get("pykotaDateLimit") 862 862 if userpquota.DateLimit is not None : 863 if userpquota.DateLimit[0].upper() == "NONE" : 863 if userpquota.DateLimit[0].upper() == "NONE" : 864 864 userpquota.DateLimit = None 865 else : 865 else : 866 866 userpquota.DateLimit = userpquota.DateLimit[0] 867 867 userpquota.Exists = True 868 868 usersandquotas.append((user, userpquota)) 869 869 self.cacheEntry("USERPQUOTAS", "%s@%s" % (user.Name, printer.Name), userpquota) 870 usersandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name)) 870 usersandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name)) 871 871 return usersandquotas 872 873 def getPrinterGroupsAndQuotas(self, printer, names=["*"]) : 872 873 def getPrinterGroupsAndQuotas(self, printer, names=["*"]) : 874 874 """Returns the list of groups which uses a given printer, along with their quotas.""" 875 875 groupsandquotas = [] … … 889 889 grouppquota = self.getGroupPQuota(group, printer) 890 890 groupsandquotas.append((group, grouppquota)) 891 groupsandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name)) 891 groupsandquotas.sort(lambda x, y : cmp(x[0].Name, y[0].Name)) 892 892 return groupsandquotas 893 893 894 894 def addPrinter(self, printer) : 895 895 """Adds a printer to the quota storage, returns the old value if it already exists.""" … … 907 907 "pykotaPricePerPage" : str(printer.PricePerPage or 0.0), 908 908 "pykotaPricePerJob" : str(printer.PricePerJob or 0.0), 909 } 909 } 910 910 dn = "%s=%s,%s" % (self.info["printerrdn"], printername, self.info["printerbase"]) 911 911 self.doAdd(dn, fields) 912 912 printer.isDirty = False 913 913 return None # the entry created doesn't need further modification 914 915 def addUser(self, user) : 914 915 def addUser(self, user) : 916 916 """Adds a user to the quota storage, returns the old value if it already exists.""" 917 917 oldentry = self.getUser(user.Name) … … 924 924 "description" : unicodeToDatabase(user.Description or ""), 925 925 self.info["usermail"] : unicodeToDatabase(user.Email or ""), 926 } 927 926 } 927 928 928 mustadd = 1 929 929 if self.info["newuser"].lower() != 'below' : … … 943 943 fields.update({ "pykotaBalance" : str(user.AccountBalance or 0.0), 944 944 "pykotaOverCharge" : str(user.OverCharge), 945 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), }) 945 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), }) 946 946 self.doModify(dn, fields) 947 947 mustadd = 0 948 948 else : 949 949 message = _("Unable to find an existing objectClass %s entry with %s=%s to attach pykotaAccount objectClass") % (where, self.info["userrdn"], user.Name) 950 if action.lower() == "warn" : 950 if action.lower() == "warn" : 951 951 self.tool.printInfo(_("%s. A new entry will be created instead.") % message, "warn") 952 952 else : # 'fail' or incorrect setting 953 953 raise PyKotaStorageError, "%s. Action aborted. Please check your configuration." % message 954 954 955 955 if mustadd : 956 if self.info["userbase"] == self.info["balancebase"] : 956 if self.info["userbase"] == self.info["balancebase"] : 957 957 fields = { self.info["userrdn"] : uname, 958 958 "objectClass" : ["pykotaObject", "pykotaAccount", "pykotaAccountBalance"], … … 960 960 "pykotaBalance" : str(user.AccountBalance or 0.0), 961 961 "pykotaOverCharge" : str(user.OverCharge), 962 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 963 } 964 else : 962 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 963 } 964 else : 965 965 fields = { self.info["userrdn"] : uname, 966 966 "objectClass" : ["pykotaObject", "pykotaAccount"], 967 967 "cn" : uname, 968 } 969 fields.update(newfields) 968 } 969 fields.update(newfields) 970 970 dn = "%s=%s,%s" % (self.info["userrdn"], uname, self.info["userbase"]) 971 971 self.doAdd(dn, fields) 972 if self.info["userbase"] != self.info["balancebase"] : 972 if self.info["userbase"] != self.info["balancebase"] : 973 973 fields = { self.info["balancerdn"] : uname, 974 974 "objectClass" : ["pykotaObject", "pykotaAccountBalance"], … … 976 976 "pykotaBalance" : str(user.AccountBalance or 0.0), 977 977 "pykotaOverCharge" : str(user.OverCharge), 978 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 979 } 978 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 979 } 980 980 dn = "%s=%s,%s" % (self.info["balancerdn"], uname, self.info["balancebase"]) 981 981 self.doAdd(dn, fields) … … 987 987 user.isDirty = False 988 988 return None # the entry created doesn't need further modification 989 990 def addGroup(self, group) : 989 990 def addGroup(self, group) : 991 991 """Adds a group to the quota storage, returns the old value if it already exists.""" 992 992 oldentry = self.getGroup(group.Name) … … 994 994 return oldentry # we return the existing entry 995 995 gname = unicodeToDatabase(group.Name) 996 newfields = { 996 newfields = { 997 997 "pykotaGroupName" : gname, 998 998 "pykotaLimitBy" : unicodeToDatabase(group.LimitBy or u"quota"), 999 999 "description" : unicodeToDatabase(group.Description or "") 1000 } 1000 } 1001 1001 mustadd = 1 1002 1002 if self.info["newgroup"].lower() != 'below' : … … 1018 1018 else : 1019 1019 message = _("Unable to find an existing entry to attach pykotaGroup objectclass %s") % group.Name 1020 if action.lower() == "warn" : 1020 if action.lower() == "warn" : 1021 1021 self.tool.printInfo("%s. A new entry will be created instead." % message, "warn") 1022 1022 else : # 'fail' or incorrect setting 1023 1023 raise PyKotaStorageError, "%s. Action aborted. Please check your configuration." % message 1024 1024 1025 1025 if mustadd : 1026 1026 fields = { self.info["grouprdn"] : gname, 1027 1027 "objectClass" : ["pykotaObject", "pykotaGroup"], 1028 1028 "cn" : gname, 1029 } 1030 fields.update(newfields) 1029 } 1030 fields.update(newfields) 1031 1031 dn = "%s=%s,%s" % (self.info["grouprdn"], gname, self.info["groupbase"]) 1032 1032 self.doAdd(dn, fields) 1033 1033 group.isDirty = False 1034 1034 return None # the entry created doesn't need further modification 1035 1036 def addUserToGroup(self, user, group) : 1035 1036 def addUserToGroup(self, user, group) : 1037 1037 """Adds an user to a group.""" 1038 1038 if user.Name not in [u.Name for u in self.getGroupMembers(group)] : 1039 result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE) 1039 result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE) 1040 1040 if result : 1041 1041 fields = result[0][1] … … 1045 1045 self.doModify(group.ident, fields) 1046 1046 group.Members.append(user) 1047 1048 def delUserFromGroup(self, user, group) : 1047 1048 def delUserFromGroup(self, user, group) : 1049 1049 """Removes an user from a group.""" 1050 1050 if user.Name in [u.Name for u in self.getGroupMembers(group)] : … … 1054 1054 if not fields.has_key(self.info["groupmembers"]) : 1055 1055 fields[self.info["groupmembers"]] = [] 1056 try : 1056 try : 1057 1057 fields[self.info["groupmembers"]].remove(unicodeToDatabase(user.Name)) 1058 1058 except ValueError : … … 1061 1061 self.doModify(group.ident, fields) 1062 1062 group.Members.remove(user) 1063 1063 1064 1064 def addUserPQuota(self, upq) : 1065 1065 """Initializes a user print quota on a printer.""" … … 1082 1082 "pykotaWarnCount" : str(upq.WarnCount or 0), 1083 1083 "pykotaMaxJobSize" : str(upq.MaxJobSize or 0), 1084 } 1084 } 1085 1085 if self.info["userquotabase"].lower() == "user" : 1086 1086 dn = "cn=%s,%s" % (uuid, upq.User.ident) 1087 else : 1087 else : 1088 1088 dn = "cn=%s,%s" % (uuid, self.info["userquotabase"]) 1089 1089 self.doAdd(dn, fields) 1090 1090 upq.isDirty = False 1091 1091 return None # the entry created doesn't need further modification 1092 1092 1093 1093 def addGroupPQuota(self, gpq) : 1094 1094 """Initializes a group print quota on a printer.""" … … 1104 1104 "pykotaPrinterName" : pname, 1105 1105 "pykotaDateLimit" : "None", 1106 } 1106 } 1107 1107 if self.info["groupquotabase"].lower() == "group" : 1108 1108 dn = "cn=%s,%s" % (uuid, gpq.Group.ident) 1109 else : 1109 else : 1110 1110 dn = "cn=%s,%s" % (uuid, self.info["groupquotabase"]) 1111 1111 self.doAdd(dn, fields) 1112 1112 gpq.isDirty = False 1113 1113 return None # the entry created doesn't need further modification 1114 1115 def savePrinter(self, printer) : 1114 1115 def savePrinter(self, printer) : 1116 1116 """Saves the printer to the database in a single operation.""" 1117 1117 fields = { … … 1123 1123 } 1124 1124 self.doModify(printer.ident, fields) 1125 1125 1126 1126 def saveUser(self, user) : 1127 1127 """Saves the user to the database in a single operation.""" 1128 1128 newfields = { 1129 1129 "pykotaLimitBy" : unicodeToDatabase(user.LimitBy or u"quota"), 1130 "description" : unicodeToDatabase(user.Description or ""), 1130 "description" : unicodeToDatabase(user.Description or ""), 1131 1131 self.info["usermail"] : user.Email or "", 1132 } 1132 } 1133 1133 self.doModify(user.ident, newfields) 1134 1134 1135 1135 newfields = { "pykotaBalance" : str(user.AccountBalance or 0.0), 1136 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 1136 "pykotaLifeTimePaid" : str(user.LifeTimePaid or 0.0), 1137 1137 "pykotaOverCharge" : str(user.OverCharge), 1138 1138 } 1139 1139 self.doModify(user.idbalance, newfields) 1140 1140 1141 1141 def saveGroup(self, group) : 1142 1142 """Saves the group to the database in a single operation.""" 1143 1143 newfields = { 1144 1144 "pykotaLimitBy" : unicodeToDatabase(group.LimitBy or u"quota"), 1145 "description" : unicodeToDatabase(group.Description or ""), 1146 } 1145 "description" : unicodeToDatabase(group.Description or ""), 1146 } 1147 1147 self.doModify(group.ident, newfields) 1148 1149 def writeUserPQuotaDateLimit(self, userpquota, datelimit) : 1148 1149 def writeUserPQuotaDateLimit(self, userpquota, datelimit) : 1150 1150 """Sets the date limit permanently for a user print quota.""" 1151 1151 fields = { … … 1153 1153 } 1154 1154 return self.doModify(userpquota.ident, fields) 1155 1156 def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) : 1155 1156 def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) : 1157 1157 """Sets the date limit permanently for a group print quota.""" 1158 1158 fields = { … … 1160 1160 } 1161 1161 return self.doModify(grouppquota.ident, fields) 1162 1163 def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) : 1162 1163 def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) : 1164 1164 """Increase page counters for a user print quota.""" 1165 1165 fields = { … … 1167 1167 "pykotaLifePageCounter" : { "operator" : "+", "value" : nbpages, "convert" : int }, 1168 1168 } 1169 return self.doModify(userpquota.ident, fields) 1170 1171 def decreaseUserAccountBalance(self, user, amount) : 1169 return self.doModify(userpquota.ident, fields) 1170 1171 def decreaseUserAccountBalance(self, user, amount) : 1172 1172 """Decreases user's account balance from an amount.""" 1173 1173 fields = { 1174 1174 "pykotaBalance" : { "operator" : "-", "value" : amount, "convert" : float }, 1175 1175 } 1176 return self.doModify(user.idbalance, fields, flushcache=1) 1177 1176 return self.doModify(user.idbalance, fields, flushcache=1) 1177 1178 1178 def writeNewPayment(self, user, amount, comment="") : 1179 1179 """Adds a new payment to the payments history.""" … … 1185 1185 "pykotaPayments" : payments, 1186 1186 } 1187 return self.doModify(user.idbalance, fields) 1188 1189 def writeLastJobSize(self, lastjob, jobsize, jobprice) : 1187 return self.doModify(user.idbalance, fields) 1188 1189 def writeLastJobSize(self, lastjob, jobsize, jobprice) : 1190 1190 """Sets the last job's size permanently.""" 1191 1191 fields = { … … 1193 1193 "pykotaJobPrice" : str(jobprice), 1194 1194 } 1195 self.doModify(lastjob.ident, fields) 1196 1195 self.doModify(lastjob.ident, fields) 1196 1197 1197 def writeJobNew(self, printer, user, jobid, pagecounter, action, jobsize=None, jobprice=None, filename=None, title=None, copies=None, options=None, clienthost=None, jobsizebytes=None, jobmd5sum=None, jobpages=None, jobbilling=None, precomputedsize=None, precomputedprice=None) : 1198 1198 """Adds a job in a printer's history.""" … … 1202 1202 uuid = self.genUUID() 1203 1203 dn = "cn=%s,%s" % (uuid, self.info["jobbase"]) 1204 else : 1204 else : 1205 1205 uuid = printer.LastJob.ident[3:].split(",")[0] 1206 1206 dn = printer.LastJob.ident 1207 if self.privacy : 1207 if self.privacy : 1208 1208 # For legal reasons, we want to hide the title, filename and options 1209 1209 title = filename = options = u"hidden" … … 1216 1216 "pykotaPrinterPageCounter" : str(pagecounter), 1217 1217 "pykotaAction" : unicodeToDatabase(action), 1218 "pykotaFileName" : ((filename is None) and "None") or unicodeToDatabase(filename), 1219 "pykotaTitle" : ((title is None) and "None") or unicodeToDatabase(title), 1220 "pykotaCopies" : str(copies), 1221 "pykotaOptions" : ((options is None) and "None") or unicodeToDatabase(options), 1222 "pykotaHostName" : str(clienthost), 1218 "pykotaFileName" : ((filename is None) and "None") or unicodeToDatabase(filename), 1219 "pykotaTitle" : ((title is None) and "None") or unicodeToDatabase(title), 1220 "pykotaCopies" : str(copies), 1221 "pykotaOptions" : ((options is None) and "None") or unicodeToDatabase(options), 1222 "pykotaHostName" : str(clienthost), 1223 1223 "pykotaJobSizeBytes" : str(jobsizebytes), 1224 1224 "pykotaMD5Sum" : unicodeToDatabase(jobmd5sum), … … 1229 1229 } 1230 1230 if (not self.disablehistory) or (not printer.LastJob.Exists) : 1231 if jobsize is not None : 1231 if jobsize is not None : 1232 1232 fields.update({ "pykotaJobSize" : str(jobsize), "pykotaJobPrice" : str(jobprice) }) 1233 1233 self.doAdd(dn, fields) 1234 else : 1234 else : 1235 1235 # here we explicitly want to reset jobsize to 'None' if needed 1236 1236 fields.update({ "pykotaJobSize" : str(jobsize), "pykotaJobPrice" : str(jobprice) }) 1237 1237 self.doModify(dn, fields) 1238 1238 1239 1239 if printer.LastJob.Exists : 1240 1240 fields = { 1241 1241 "pykotaLastJobIdent" : uuid, 1242 1242 } 1243 self.doModify(printer.LastJob.lastjobident, fields) 1244 else : 1243 self.doModify(printer.LastJob.lastjobident, fields) 1244 else : 1245 1245 lastjuuid = self.genUUID() 1246 1246 lastjdn = "cn=%s,%s" % (lastjuuid, self.info["lastjobbase"]) … … 1250 1250 "pykotaPrinterName" : pname, 1251 1251 "pykotaLastJobIdent" : uuid, 1252 } 1253 self.doAdd(lastjdn, fields) 1254 1252 } 1253 self.doAdd(lastjdn, fields) 1254 1255 1255 def saveUserPQuota(self, userpquota) : 1256 1256 """Saves an user print quota entry.""" 1257 fields = { 1257 fields = { 1258 1258 "pykotaSoftLimit" : str(userpquota.SoftLimit), 1259 1259 "pykotaHardLimit" : str(userpquota.HardLimit), … … 1265 1265 } 1266 1266 self.doModify(userpquota.ident, fields) 1267 1267 1268 1268 def writeUserPQuotaWarnCount(self, userpquota, warncount) : 1269 1269 """Sets the warn counter value for a user quota.""" 1270 fields = { 1270 fields = { 1271 1271 "pykotaWarnCount" : str(warncount or 0), 1272 1272 } 1273 1273 self.doModify(userpquota.ident, fields) 1274 1274 1275 1275 def increaseUserPQuotaWarnCount(self, userpquota) : 1276 1276 """Increases the warn counter value for a user quota.""" … … 1278 1278 "pykotaWarnCount" : { "operator" : "+", "value" : 1, "convert" : int }, 1279 1279 } 1280 return self.doModify(userpquota.ident, fields) 1281 1280 return self.doModify(userpquota.ident, fields) 1281 1282 1282 def saveGroupPQuota(self, grouppquota) : 1283 1283 """Saves a group print quota entry.""" 1284 fields = { 1284 fields = { 1285 1285 "pykotaSoftLimit" : str(grouppquota.SoftLimit), 1286 1286 "pykotaHardLimit" : str(grouppquota.HardLimit), … … 1289 1289 } 1290 1290 self.doModify(grouppquota.ident, fields) 1291 1291 1292 1292 def writePrinterToGroup(self, pgroup, printer) : 1293 1293 """Puts a printer into a printer group.""" … … 1296 1296 fields = { 1297 1297 "uniqueMember" : pgroup.uniqueMember 1298 } 1299 self.doModify(pgroup.ident, fields) 1300 1298 } 1299 self.doModify(pgroup.ident, fields) 1300 1301 1301 def removePrinterFromGroup(self, pgroup, printer) : 1302 1302 """Removes a printer from a printer group.""" 1303 1303 try : 1304 1304 pgroup.uniqueMember.remove(printer.ident) 1305 except ValueError : 1305 except ValueError : 1306 1306 pass 1307 else : 1307 else : 1308 1308 fields = { 1309 1309 "uniqueMember" : pgroup.uniqueMember, 1310 } 1311 self.doModify(pgroup.ident, fields) 1312 1310 } 1311 self.doModify(pgroup.ident, fields) 1312 1313 1313 def retrieveHistory(self, user=None, printer=None, hostname=None, billingcode=None, jobid=None, limit=100, start=None, end=None) : 1314 1314 """Retrieves all print jobs for user on printer (or all) between start and end date, limited to first 100 results.""" … … 1325 1325 if jobid is not None : 1326 1326 where.append("(pykotaJobId=%s)" % jobid) # TODO : jobid is text, so unicodeToDatabase(jobid) but do all of them as well. 1327 if where : 1327 if where : 1328 1328 where = "(&%s)" % "".join([precond] + where) 1329 else : 1329 else : 1330 1330 where = precond 1331 jobs = [] 1332 result = self.doSearch(where, fields=[ "pykotaJobSizeBytes", 1333 "pykotaHostName", 1334 "pykotaUserName", 1335 "pykotaPrinterName", 1336 "pykotaJobId", 1337 "pykotaPrinterPageCounter", 1338 "pykotaAction", 1339 "pykotaJobSize", 1340 "pykotaJobPrice", 1341 "pykotaFileName", 1342 "pykotaTitle", 1343 "pykotaCopies", 1344 "pykotaOptions", 1345 "pykotaBillingCode", 1346 "pykotaPages", 1347 "pykotaMD5Sum", 1331 jobs = [] 1332 result = self.doSearch(where, fields=[ "pykotaJobSizeBytes", 1333 "pykotaHostName", 1334 "pykotaUserName", 1335 "pykotaPrinterName", 1336 "pykotaJobId", 1337 "pykotaPrinterPageCounter", 1338 "pykotaAction", 1339 "pykotaJobSize", 1340 "pykotaJobPrice", 1341 "pykotaFileName", 1342 "pykotaTitle", 1343 "pykotaCopies", 1344 "pykotaOptions", 1345 "pykotaBillingCode", 1346 "pykotaPages", 1347 "pykotaMD5Sum", 1348 1348 "pykotaPrecomputedJobSize", 1349 1349 "pykotaPrecomputedJobPrice", 1350 "createTimestamp" ], 1350 "createTimestamp" ], 1351 1351 base=self.info["jobbase"]) 1352 1352 if result : … … 1358 1358 try : 1359 1359 job.JobSize = int(fields.get("pykotaJobSize", [0])[0]) 1360 except ValueError : 1360 except ValueError : 1361 1361 job.JobSize = None 1362 try : 1362 try : 1363 1363 job.JobPrice = float(fields.get("pykotaJobPrice", [0.0])[0]) 1364 1364 except ValueError : 1365 1365 job.JobPrice = None 1366 1366 job.JobAction = databaseToUnicode(fields.get("pykotaAction", [""])[0]) 1367 job.JobFileName = databaseToUnicode(fields.get("pykotaFileName", [""])[0]) 1368 job.JobTitle = databaseToUnicode(fields.get("pykotaTitle", [""])[0]) 1367 job.JobFileName = databaseToUnicode(fields.get("pykotaFileName", [""])[0]) 1368 job.JobTitle = databaseToUnicode(fields.get("pykotaTitle", [""])[0]) 1369 1369 job.JobCopies = int(fields.get("pykotaCopies", [0])[0]) 1370 job.JobOptions = databaseToUnicode(fields.get("pykotaOptions", [""])[0]) 1370 job.JobOptions = databaseToUnicode(fields.get("pykotaOptions", [""])[0]) 1371 1371 job.JobHostName = databaseToUnicode(fields.get("pykotaHostName", [""])[0]) 1372 1372 job.JobSizeBytes = fields.get("pykotaJobSizeBytes", [0L])[0] … … 1376 1376 try : 1377 1377 job.PrecomputedJobSize = int(fields.get("pykotaPrecomputedJobSize", [0])[0]) 1378 except ValueError : 1378 except ValueError : 1379 1379 job.PrecomputedJobSize = None 1380 try : 1380 try : 1381 1381 job.PrecomputedJobPrice = float(fields.get("pykotaPrecomputedJobPrice", [0.0])[0]) 1382 1382 except ValueError : … … 1395 1395 job.Exists = True 1396 1396 jobs.append(job) 1397 jobs.sort(lambda x, y : cmp(y.JobDate, x.JobDate)) 1398 if limit : 1397 jobs.sort(lambda x, y : cmp(y.JobDate, x.JobDate)) 1398 if limit : 1399 1399 jobs = jobs[:int(limit)] 1400 1400 return jobs 1401 1402 def deleteUser(self, user) : 1401 1402 def deleteUser(self, user) : 1403 1403 """Completely deletes an user from the Quota Storage.""" 1404 1404 uname = unicodeToDatabase(user.Name) 1405 todelete = [] 1405 todelete = [] 1406 1406 result = self.doSearch("(&(objectClass=pykotaJob)(pykotaUserName=%s))" % uname, base=self.info["jobbase"]) 1407 1407 for (ident, fields) in result : … … 1417 1417 # ensure the user print quota entry will be deleted 1418 1418 todelete.append(ident) 1419 1419 1420 1420 # if last job of current printer was printed by the user 1421 1421 # to delete, we also need to delete the printer's last job entry. … … 1423 1423 if printer.LastJob.UserName == user.Name : 1424 1424 todelete.append(printer.LastJob.lastjobident) 1425 1426 for ident in todelete : 1425 1426 for ident in todelete : 1427 1427 self.doDelete(ident) 1428 1429 result = self.doSearch("objectClass=pykotaAccount", None, base=user.ident, scope=ldap.SCOPE_BASE) 1428 1429 result = self.doSearch("objectClass=pykotaAccount", None, base=user.ident, scope=ldap.SCOPE_BASE) 1430 1430 if result : 1431 1431 fields = result[0][1] … … 1433 1433 if k.startswith("pykota") : 1434 1434 del fields[k] 1435 elif k.lower() == "objectclass" : 1435 elif k.lower() == "objectclass" : 1436 1436 todelete = [] 1437 1437 for i in range(len(fields[k])) : 1438 if fields[k][i].startswith("pykota") : 1438 if fields[k][i].startswith("pykota") : 1439 1439 todelete.append(i) 1440 todelete.sort() 1440 todelete.sort() 1441 1441 todelete.reverse() 1442 1442 for i in todelete : 1443 1443 del fields[k][i] 1444 1444 if fields.get("objectClass") or fields.get("objectclass") : 1445 self.doModify(user.ident, fields, ignoreold=0) 1446 else : 1445 self.doModify(user.ident, fields, ignoreold=0) 1446 else : 1447 1447 self.doDelete(user.ident) 1448 1448 result = self.doSearch("(&(objectClass=pykotaAccountBalance)(pykotaUserName=%s))" % \ … … 1452 1452 for (ident, fields) in result : 1453 1453 self.doDelete(ident) 1454 1455 def deleteGroup(self, group) : 1454 1455 def deleteGroup(self, group) : 1456 1456 """Completely deletes a group from the Quota Storage.""" 1457 1457 gname = unicodeToDatabase(group.Name) … … 1466 1466 for (ident, fields) in result : 1467 1467 self.doDelete(ident) 1468 result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE) 1468 result = self.doSearch("objectClass=pykotaGroup", None, base=group.ident, scope=ldap.SCOPE_BASE) 1469 1469 if result : 1470 1470 fields = result[0][1] … … 1472 1472 if k.startswith("pykota") : 1473 1473 del fields[k] 1474 elif k.lower() == "objectclass" : 1474 elif k.lower() == "objectclass" : 1475 1475 todelete = [] 1476 1476 for i in range(len(fields[k])) : 1477 if fields[k][i].startswith("pykota") : 1477 if fields[k][i].startswith("pykota") : 1478 1478 todelete.append(i) 1479 todelete.sort() 1479 todelete.sort() 1480 1480 todelete.reverse() 1481 1481 for i in todelete : 1482 1482 del fields[k][i] 1483 1483 if fields.get("objectClass") or fields.get("objectclass") : 1484 self.doModify(group.ident, fields, ignoreold=0) 1485 else : 1484 self.doModify(group.ident, fields, ignoreold=0) 1485 else : 1486 1486 self.doDelete(group.ident) 1487 1487 1488 1488 def deleteManyBillingCodes(self, billingcodes) : 1489 1489 """Deletes many billing codes.""" 1490 1490 for bcode in billingcodes : 1491 1491 bcode.delete() 1492 1493 def deleteManyUsers(self, users) : 1492 1493 def deleteManyUsers(self, users) : 1494 1494 """Deletes many users.""" 1495 1495 for user in users : 1496 1496 user.delete() 1497 1498 def deleteManyGroups(self, groups) : 1497 1498 def deleteManyGroups(self, groups) : 1499 1499 """Deletes many groups.""" 1500 1500 for group in groups : 1501 1501 group.delete() 1502 1503 def deleteManyPrinters(self, printers) : 1502 1503 def deleteManyPrinters(self, printers) : 1504 1504 """Deletes many printers.""" 1505 1505 for printer in printers : 1506 1506 printer.delete() 1507 1508 def deleteManyUserPQuotas(self, printers, users) : 1507 1508 def deleteManyUserPQuotas(self, printers, users) : 1509 1509 """Deletes many user print quota entries.""" 1510 1510 # TODO : grab all with a single (possibly VERY huge) filter if possible (might depend on the LDAP server !) … … 1514 1514 if upq.Exists : 1515 1515 upq.delete() 1516 1516 1517 1517 def deleteManyGroupPQuotas(self, printers, groups) : 1518 1518 """Deletes many group print quota entries.""" … … 1523 1523 if gpq.Exists : 1524 1524 gpq.delete() 1525 1526 def deleteUserPQuota(self, upquota) : 1525 1526 def deleteUserPQuota(self, upquota) : 1527 1527 """Completely deletes an user print quota entry from the database.""" 1528 1528 uname = unicodeToDatabase(upquota.User.Name) … … 1536 1536 self.doDelete(upquota.Printer.LastJob.lastjobident) 1537 1537 self.doDelete(upquota.ident) 1538 1539 def deleteGroupPQuota(self, gpquota) : 1538 1539 def deleteGroupPQuota(self, gpquota) : 1540 1540 """Completely deletes a group print quota entry from the database.""" 1541 1541 self.doDelete(gpquota.ident) 1542 1543 def deletePrinter(self, printer) : 1542 1543 def deletePrinter(self, printer) : 1544 1544 """Completely deletes a printer from the Quota Storage.""" 1545 1545 pname = unicodeToDatabase(printer.Name) … … 1564 1564 for (ident, fields) in result : 1565 1565 self.doDelete(ident) 1566 for parent in self.getParentPrinters(printer) : 1566 for parent in self.getParentPrinters(printer) : 1567 1567 try : 1568 1568 parent.uniqueMember.remove(printer.ident) 1569 except ValueError : 1569 except ValueError : 1570 1570 pass 1571 else : 1571 else : 1572 1572 fields = { 1573 1573 "uniqueMember" : parent.uniqueMember, 1574 } 1575 self.doModify(parent.ident, fields) 1576 self.doDelete(printer.ident) 1577 1574 } 1575 self.doModify(parent.ident, fields) 1576 self.doDelete(printer.ident) 1577 1578 1578 def deleteBillingCode(self, code) : 1579 1579 """Deletes a billing code from the Quota Storage (no entries are deleted from the history)""" 1580 1580 self.doDelete(code.ident) 1581 1582 def sortRecords(self, fields, records, default, ordering) : 1581 1582 def sortRecords(self, fields, records, default, ordering) : 1583 1583 """Sort records based on list of fields prefixed with '+' (ASC) or '-' (DESC).""" 1584 1584 fieldindexes = {} 1585 1585 for i in range(len(fields)) : 1586 1586 fieldindexes[fields[i]] = i 1587 if not ordering : 1587 if not ordering : 1588 1588 ordering = default 1589 orderby = [] 1589 orderby = [] 1590 1590 for orderkey in ordering : 1591 1591 # Create ordering hints, ignoring unknown fields … … 1598 1598 if index is not None : 1599 1599 orderby.append((+1, index)) 1600 else : 1600 else : 1601 1601 index = fieldindexes.get(orderkey) 1602 1602 if index is not None : 1603 1603 orderby.append((+1, index)) 1604 1605 def compare(x, y, orderby=orderby) : 1604 1605 def compare(x, y, orderby=orderby) : 1606 1606 """Compares two records.""" 1607 1607 i = 0 … … 1612 1612 if not result : 1613 1613 i += 1 1614 else : 1614 else : 1615 1615 return sign * result 1616 return 0 # identical keys 1617 1616 return 0 # identical keys 1617 1618 1618 records.sort(compare) 1619 1619 return records 1620 1620 1621 1621 def extractPrinters(self, extractonly={}, ordering=[]) : 1622 1622 """Extracts all printer records.""" … … 1629 1629 if entry.PassThrough in (1, "1", "t", "true", "T", "TRUE", "True") : 1630 1630 passthrough = "t" 1631 else : 1631 else : 1632 1632 passthrough = "f" 1633 1633 result.append((entry.ident, entry.Name, entry.PricePerPage, entry.PricePerJob, entry.Description, entry.MaxJobSize, passthrough)) 1634 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1635 1634 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1635 1636 1636 def extractUsers(self, extractonly={}, ordering=[]) : 1637 1637 """Extracts all user records.""" … … 1644 1644 result.append((entry.ident, entry.Name, entry.AccountBalance, entry.LifeTimePaid, entry.LimitBy, entry.Email, entry.Description, entry.OverCharge)) 1645 1645 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1646 1646 1647 1647 def extractBillingcodes(self, extractonly={}, ordering=[]) : 1648 1648 """Extracts all billing codes records.""" … … 1655 1655 result.append((entry.ident, entry.BillingCode, entry.Balance, entry.PageCounter, entry.Description)) 1656 1656 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1657 1657 1658 1658 def extractGroups(self, extractonly={}, ordering=[]) : 1659 1659 """Extracts all group records.""" … … 1666 1666 result.append((entry.ident, entry.Name, entry.LimitBy, entry.AccountBalance, entry.LifeTimePaid, entry.Description)) 1667 1667 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1668 1668 1669 1669 def extractPayments(self, extractonly={}, ordering=[]) : 1670 1670 """Extracts all payment records.""" … … 1685 1685 result.append((entry.Name, amount, date, description)) 1686 1686 return [fields] + self.sortRecords(fields, result, ["+date"], ordering) 1687 1687 1688 1688 def extractUpquotas(self, extractonly={}, ordering=[]) : 1689 1689 """Extracts all userpquota records.""" … … 1698 1698 result.append((user.Name, entry.Name, userpquota.ident, user.ident, entry.ident, userpquota.LifePageCounter, userpquota.PageCounter, userpquota.SoftLimit, userpquota.HardLimit, userpquota.DateLimit)) 1699 1699 return [fields] + self.sortRecords(fields, result, ["+userdn"], ordering) 1700 1700 1701 1701 def extractGpquotas(self, extractonly={}, ordering=[]) : 1702 1702 """Extracts all grouppquota records.""" … … 1711 1711 result.append((group.Name, entry.Name, grouppquota.ident, group.ident, entry.ident, grouppquota.LifePageCounter, grouppquota.PageCounter, grouppquota.SoftLimit, grouppquota.HardLimit, grouppquota.DateLimit)) 1712 1712 return [fields] + self.sortRecords(fields, result, ["+groupdn"], ordering) 1713 1713 1714 1714 def extractUmembers(self, extractonly={}, ordering=[]) : 1715 1715 """Extracts all user groups members.""" … … 1725 1725 result.append((entry.Name, member.Name, entry.ident, member.ident)) 1726 1726 return [fields] + self.sortRecords(fields, result, ["+groupdn", "+userdn"], ordering) 1727 1727 1728 1728 def extractPmembers(self, extractonly={}, ordering=[]) : 1729 1729 """Extracts all printer groups members.""" … … 1739 1739 result.append((parent.Name, entry.Name, parent.ident, entry.ident)) 1740 1740 return [fields] + self.sortRecords(fields, result, ["+pgroupdn", "+printerdn"], ordering) 1741 1741 1742 1742 def extractHistory(self, extractonly={}, ordering=[]) : 1743 1743 """Extracts all jobhistory records.""" … … 1745 1745 if uname : 1746 1746 user = self.getUser(uname) 1747 else : 1747 else : 1748 1748 user = None 1749 1749 pname = extractonly.get("printername") 1750 1750 if pname : 1751 1751 printer = self.getPrinter(pname) 1752 else : 1752 else : 1753 1753 printer = None 1754 1754 startdate = extractonly.get("start") … … 1760 1760 result = [] 1761 1761 for entry in entries : 1762 result.append((entry.UserName, entry.PrinterName, entry.ident, entry.JobId, entry.PrinterPageCounter, entry.JobSize, entry.JobAction, entry.JobDate, entry.JobFileName, entry.JobTitle, entry.JobCopies, entry.JobOptions, entry.JobPrice, entry.JobHostName, entry.JobSizeBytes, entry.JobMD5Sum, entry.JobPages, entry.JobBillingCode, entry.PrecomputedJobSize, entry.PrecomputedJobPrice)) 1762 result.append((entry.UserName, entry.PrinterName, entry.ident, entry.JobId, entry.PrinterPageCounter, entry.JobSize, entry.JobAction, entry.JobDate, entry.JobFileName, entry.JobTitle, entry.JobCopies, entry.JobOptions, entry.JobPrice, entry.JobHostName, entry.JobSizeBytes, entry.JobMD5Sum, entry.JobPages, entry.JobBillingCode, entry.PrecomputedJobSize, entry.PrecomputedJobPrice)) 1763 1763 return [fields] + self.sortRecords(fields, result, ["+dn"], ordering) 1764 1764 1765 1765 def getBillingCodeFromBackend(self, label) : 1766 1766 """Extracts billing code information given its label : returns first matching billing code.""" … … 1777 1777 code.PageCounter = int(fields.get("pykotaPageCounter", [0])[0]) 1778 1778 code.Balance = float(fields.get("pykotaBalance", [0.0])[0]) 1779 code.Description = databaseToUnicode(fields.get("description", [""])[0]) 1779 code.Description = databaseToUnicode(fields.get("description", [""])[0]) 1780 1780 code.Exists = True 1781 return code 1782 1781 return code 1782 1783 1783 def addBillingCode(self, bcode) : 1784 1784 """Adds a billing code to the quota storage, returns it.""" … … 1793 1793 "pykotaPageCounter" : str(bcode.PageCounter or 0), 1794 1794 "pykotaBalance" : str(bcode.Balance or 0.0), 1795 "description" : unicodeToDatabase(bcode.Description or ""), 1796 } 1795 "description" : unicodeToDatabase(bcode.Description or ""), 1796 } 1797 1797 self.doAdd(dn, fields) 1798 1798 bcode.isDirty = False 1799 1799 return None # the entry created doesn't need further modification 1800 1800 1801 1801 def saveBillingCode(self, bcode) : 1802 1802 """Sets the new description for a billing code.""" 1803 1803 fields = { 1804 "description" : unicodeToDatabase(bcode.Description or ""), 1804 "description" : unicodeToDatabase(bcode.Description or ""), 1805 1805 "pykotaPageCounter" : str(bcode.PageCounter or 0), 1806 1806 "pykotaBalance" : str(bcode.Balance or 0.0), 1807 1807 } 1808 1808 self.doModify(bcode.ident, fields) 1809 1809 1810 1810 def getMatchingBillingCodes(self, billingcodepattern) : 1811 1811 """Returns the list of all billing codes which match a certain pattern.""" … … 1818 1818 try : 1819 1819 patdict = {}.fromkeys(patterns) 1820 except AttributeError : 1820 except AttributeError : 1821 1821 # Python v2.2 or earlier 1822 1822 patdict = {} … … 1830 1830 code.PageCounter = int(fields.get("pykotaPageCounter", [0])[0]) 1831 1831 code.Balance = float(fields.get("pykotaBalance", [0.0])[0]) 1832 code.Description = databaseToUnicode(fields.get("description", [""])[0]) 1832 code.Description = databaseToUnicode(fields.get("description", [""])[0]) 1833 1833 code.Exists = True 1834 1834 codes.append(code) 1835 1835 self.cacheEntry("BILLINGCODES", code.BillingCode, code) 1836 return codes 1837 1836 return codes 1837 1838 1838 def consumeBillingCode(self, bcode, pagecounter, balance) : 1839 1839 """Consumes from a billing code.""" … … 1842 1842 "pykotaPageCounter" : { "operator" : "+", "value" : pagecounter, "convert" : int }, 1843 1843 } 1844 return self.doModify(bcode.ident, fields) 1845 1846 def refundJob(self, jobident) : 1844 return self.doModify(bcode.ident, fields) 1845 1846 def refundJob(self, jobident) : 1847 1847 """Marks a job as refunded in the history.""" 1848 1848 fields = { 1849 1849 "pykotaAction" : "REFUND", 1850 } 1851 self.doModify(jobident, fields) 1852 1850 } 1851 self.doModify(jobident, fields) 1852 1853 1853 def storageUserFromRecord(self, username, record) : 1854 1854 """Returns a StorageUser instance from a database record.""" … … 1856 1856 user.Exists = True 1857 1857 return user 1858 1858 1859 1859 def storageGroupFromRecord(self, groupname, record) : 1860 1860 """Returns a StorageGroup instance from a database record.""" … … 1862 1862 group.Exists = True 1863 1863 return group 1864 1864 1865 1865 def storagePrinterFromRecord(self, printername, record) : 1866 1866 """Returns a StoragePrinter instance from a database record.""" … … 1868 1868 printer.Exists = True 1869 1869 return printer 1870 1871 def setJobAttributesFromRecord(self, job, record) : 1870 1871 def setJobAttributesFromRecord(self, job, record) : 1872 1872 """Sets the attributes of a job from a database record.""" 1873 1873 job.Exists = True 1874 1874 1875 1875 def storageJobFromRecord(self, record) : 1876 1876 """Returns a StorageJob instance from a database record.""" … … 1878 1878 self.setJobAttributesFromRecord(job, record) 1879 1879 return job 1880 1880 1881 1881 def storageLastJobFromRecord(self, printer, record) : 1882 1882 """Returns a StorageLastJob instance from a database record.""" … … 1884 1884 self.setJobAttributesFromRecord(lastjob, record) 1885 1885 return lastjob 1886 1886 1887 1887 def storageUserPQuotaFromRecord(self, user, printer, record) : 1888 1888 """Returns a StorageUserPQuota instance from a database record.""" … … 1890 1890 userpquota.Exists = True 1891 1891 return userpquota 1892 1892 1893 1893 def storageGroupPQuotaFromRecord(self, group, printer, record) : 1894 1894 """Returns a StorageGroupPQuota instance from a database record.""" … … 1896 1896 grouppquota.Exists = True 1897 1897 return grouppquota 1898 1898 1899 1899 def storageBillingCodeFromRecord(self, billingcode, record) : 1900 1900 """Returns a StorageBillingCode instance from a database record.""" -
pykota/trunk/pykota/storages/mysqlstorage.py
r3411 r3413 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 29 29 try : 30 30 import MySQLdb 31 except ImportError : 31 except ImportError : 32 32 import sys 33 33 # TODO : to translate or not to translate ? … … 41 41 (host, port) = host.split(":") 42 42 port = int(port) 43 except ValueError : 43 except ValueError : 44 44 port = 3306 # Use the default MySQL port 45 45 46 46 self.tool.logdebug("Trying to open database (host=%s, port=%s, dbname=%s, user=%s)..." % (host, port, dbname, user)) 47 47 try : 48 48 self.database = MySQLdb.connect(host=host, port=port, db=dbname, user=user, passwd=passwd, charset="utf8") 49 except TypeError : 49 except TypeError : 50 50 self.tool.logdebug("'charset' argument not allowed with this version of python-mysqldb, retrying without...") 51 51 self.database = MySQLdb.connect(host=host, port=port, db=dbname, user=user, passwd=passwd) 52 52 53 53 try : 54 54 self.database.autocommit(1) 55 except AttributeError : 55 except AttributeError : 56 56 raise PyKotaStorageError, _("Your version of python-mysqldb is too old. Please install a newer release.") 57 57 self.cursor = self.database.cursor() … … 62 62 try : 63 63 # Here we try to select a string (an é) which is 64 # already encoded in UTF-8. If python-mysqldb suffers from 64 # already encoded in UTF-8. If python-mysqldb suffers from 65 65 # the double encoding problem, we will catch the exception 66 66 # and activate a workaround. 67 67 self.cursor.execute("SELECT '%s';" % (chr(0xc3) + chr(0xa9))) # é in UTF-8 68 68 self.cursor.fetchall() 69 except UnicodeDecodeError : 69 except UnicodeDecodeError : 70 70 self.needsworkaround = True 71 71 self.tool.logdebug("Database needs encoding workaround.") … … 73 73 self.needsworkaround = False 74 74 self.tool.logdebug("Database doesn't need encoding workaround.") 75 76 def close(self) : 75 76 def close(self) : 77 77 """Closes the database connection.""" 78 78 if not self.closed : … … 81 81 self.closed = True 82 82 self.tool.logdebug("Database closed.") 83 84 def beginTransaction(self) : 83 84 def beginTransaction(self) : 85 85 """Starts a transaction.""" 86 86 self.cursor.execute("BEGIN;") 87 87 self.tool.logdebug("Transaction begins...") 88 89 def commitTransaction(self) : 88 89 def commitTransaction(self) : 90 90 """Commits a transaction.""" 91 91 self.database.commit() 92 92 self.tool.logdebug("Transaction committed.") 93 94 def rollbackTransaction(self) : 93 94 def rollbackTransaction(self) : 95 95 """Rollbacks a transaction.""" 96 96 self.database.rollback() 97 97 self.tool.logdebug("Transaction aborted.") 98 98 99 99 def doRawSearch(self, query) : 100 100 """Does a raw search query.""" 101 query = query.strip() 102 if not query.endswith(';') : 101 query = query.strip() 102 if not query.endswith(';') : 103 103 query += ';' 104 104 self.querydebug("QUERY : %s" % query) 105 if self.needsworkaround : 105 if self.needsworkaround : 106 106 query = query.decode("UTF-8") 107 107 try : 108 108 self.cursor.execute(query) 109 except self.database.Error, msg : 109 except self.database.Error, msg : 110 110 raise PyKotaStorageError, repr(msg) 111 else : 111 else : 112 112 # This returns a list of lists. Integers are returned as longs. 113 113 return self.cursor.fetchall() 114 115 def doSearch(self, query) : 114 115 def doSearch(self, query) : 116 116 """Does a search query.""" 117 117 result = self.doRawSearch(query) … … 131 131 def doModify(self, query) : 132 132 """Does a (possibly multiple) modify query.""" 133 query = query.strip() 134 if not query.endswith(';') : 133 query = query.strip() 134 if not query.endswith(';') : 135 135 query += ';' 136 136 self.querydebug("QUERY : %s" % query) 137 if self.needsworkaround : 137 if self.needsworkaround : 138 138 query = query.decode("UTF-8") 139 139 try : 140 140 self.cursor.execute(query) 141 except self.database.Error, msg : 141 except self.database.Error, msg : 142 142 self.tool.logdebug("Query failed : %s" % repr(msg)) 143 143 raise PyKotaStorageError, repr(msg) 144 144 145 145 def doQuote(self, field) : 146 146 """Quotes a field for use as a string in SQL queries.""" -
pykota/trunk/pykota/storages/pgstorage.py
r3411 r3413 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 29 29 from pykota.storages.sql import SQLStorage 30 30 31 from pykota.utils import * 31 from pykota.utils import * 32 32 33 33 try : 34 34 import pg 35 except ImportError : 35 except ImportError : 36 36 import sys 37 37 # TODO : to translate or not to translate ? 38 38 raise PyKotaStorageError, "This python version (%s) doesn't seem to have the PygreSQL module installed correctly." % sys.version.split()[0] 39 else : 39 else : 40 40 try : 41 41 PGError = pg.Error 42 except AttributeError : 42 except AttributeError : 43 43 PGError = pg.error 44 44 … … 50 50 (host, port) = host.split(":") 51 51 port = int(port) 52 except ValueError : 52 except ValueError : 53 53 port = 5432 # Use PostgreSQL's default tcp/ip port (5432). 54 54 55 55 self.tool.logdebug("Trying to open database (host=%s, port=%s, dbname=%s, user=%s)..." % (host, port, dbname, user)) 56 56 try : 57 57 self.database = pg.connect(host=host, port=port, dbname=dbname, user=user, passwd=passwd) 58 except PGError, msg : 58 except PGError, msg : 59 59 msg = "%(msg)s --- the most probable cause of your problem is that PostgreSQL is down, or doesn't accept incoming connections because you didn't configure it as explained in PyKota's documentation." % locals() 60 60 raise PGError, msg … … 62 62 try : 63 63 self.database.query("SET CLIENT_ENCODING TO 'UTF-8';") 64 except PGError, msg : 64 except PGError, msg : 65 65 self.tool.logdebug("Impossible to set database client encoding to UTF-8 : %s" % msg) 66 66 self.tool.logdebug("Database opened (host=%s, port=%s, dbname=%s, user=%s)" % (host, port, dbname, user)) 67 68 def close(self) : 67 68 def close(self) : 69 69 """Closes the database connection.""" 70 70 if not self.closed : … … 72 72 self.closed = 1 73 73 self.tool.logdebug("Database closed.") 74 75 def beginTransaction(self) : 74 75 def beginTransaction(self) : 76 76 """Starts a transaction.""" 77 77 self.database.query("BEGIN;") 78 78 self.tool.logdebug("Transaction begins...") 79 80 def commitTransaction(self) : 79 80 def commitTransaction(self) : 81 81 """Commits a transaction.""" 82 82 self.database.query("COMMIT;") 83 83 self.tool.logdebug("Transaction committed.") 84 85 def rollbackTransaction(self) : 84 85 def rollbackTransaction(self) : 86 86 """Rollbacks a transaction.""" 87 87 self.database.query("ROLLBACK;") 88 88 self.tool.logdebug("Transaction aborted.") 89 89 90 90 def doRawSearch(self, query) : 91 91 """Does a raw search query.""" 92 query = query.strip() 93 if not query.endswith(';') : 92 query = query.strip() 93 if not query.endswith(';') : 94 94 query += ';' 95 95 try : 96 96 self.querydebug("QUERY : %s" % query) 97 97 return self.database.query(query) 98 except PGError, msg : 98 except PGError, msg : 99 99 raise PyKotaStorageError, repr(msg) 100 101 def doSearch(self, query) : 100 101 def doSearch(self, query) : 102 102 """Does a search query.""" 103 103 result = self.doRawSearch(query) 104 if (result is not None) and (result.ntuples() > 0) : 104 if (result is not None) and (result.ntuples() > 0) : 105 105 return result.dictresult() 106 106 107 107 def doModify(self, query) : 108 108 """Does a (possibly multiple) modify query.""" 109 query = query.strip() 110 if not query.endswith(';') : 109 query = query.strip() 110 if not query.endswith(';') : 111 111 query += ';' 112 112 try : 113 113 self.querydebug("QUERY : %s" % query) 114 114 return self.database.query(query) 115 except PGError, msg : 115 except PGError, msg : 116 116 self.tool.logdebug("Query failed : %s" % repr(msg)) 117 117 raise PyKotaStorageError, repr(msg) 118 118 119 119 def doQuote(self, field) : 120 120 """Quotes a field for use as a string in SQL queries.""" 121 if type(field) == type(0.0) : 121 if type(field) == type(0.0) : 122 122 typ = "decimal" 123 elif type(field) == type(0) : 123 elif type(field) == type(0) : 124 124 typ = "int" 125 elif type(field) == type(0L) : 125 elif type(field) == type(0L) : 126 126 typ = "int" 127 else : 127 else : 128 128 typ = "text" 129 129 return pg._quote(field, typ) 130 130 131 131 def prepareRawResult(self, result) : 132 132 """Prepares a raw result by including the headers.""" … … 140 140 field = fields[j] 141 141 if type(field) == StringType : 142 fields[j] = databaseToUnicode(field) 143 entries[i] = tuple(fields) 142 fields[j] = databaseToUnicode(field) 143 entries[i] = tuple(fields) 144 144 return entries 145 145 -
pykota/trunk/pykota/storages/sqlitestorage.py
r3411 r3413 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 29 29 try : 30 30 from pysqlite2 import dbapi2 as sqlite 31 except ImportError : 31 except ImportError : 32 32 import sys 33 33 # TODO : to translate or not to translate ? 34 34 raise PyKotaStorageError, "This python version (%s) doesn't seem to have the PySQLite module installed correctly." % sys.version.split()[0] 35 35 36 36 class Storage(BaseStorage, SQLStorage) : 37 37 def __init__(self, pykotatool, host, dbname, user, passwd) : 38 38 """Opens the SQLite database connection.""" 39 39 BaseStorage.__init__(self, pykotatool) 40 40 41 41 self.tool.logdebug("Trying to open database (dbname=%s)..." % dbname) 42 42 self.database = sqlite.connect(dbname, isolation_level=None) … … 44 44 self.closed = 0 45 45 self.tool.logdebug("Database opened (dbname=%s)" % dbname) 46 47 def close(self) : 46 47 def close(self) : 48 48 """Closes the database connection.""" 49 49 if not self.closed : … … 52 52 self.closed = 1 53 53 self.tool.logdebug("Database closed.") 54 55 def beginTransaction(self) : 54 55 def beginTransaction(self) : 56 56 """Starts a transaction.""" 57 57 self.cursor.execute("BEGIN;") 58 58 self.tool.logdebug("Transaction begins...") 59 60 def commitTransaction(self) : 59 60 def commitTransaction(self) : 61 61 """Commits a transaction.""" 62 62 self.cursor.execute("COMMIT;") 63 63 self.tool.logdebug("Transaction committed.") 64 65 def rollbackTransaction(self) : 64 65 def rollbackTransaction(self) : 66 66 """Rollbacks a transaction.""" 67 67 self.cursor.execute("ROLLBACK;") 68 68 self.tool.logdebug("Transaction aborted.") 69 69 70 70 def doRawSearch(self, query) : 71 71 """Does a raw search query.""" 72 query = query.strip() 73 if not query.endswith(';') : 72 query = query.strip() 73 if not query.endswith(';') : 74 74 query += ';' 75 75 try : 76 76 self.querydebug("QUERY : %s" % query) 77 77 self.cursor.execute(query) 78 except self.database.Error, msg : 78 except self.database.Error, msg : 79 79 raise PyKotaStorageError, repr(msg) 80 else : 80 else : 81 81 result = self.cursor.fetchall() 82 82 return result 83 84 def doSearch(self, query) : 83 84 def doSearch(self, query) : 85 85 """Does a search query.""" 86 86 result = self.doRawSearch(query) 87 if result : 87 if result : 88 88 rows = [] 89 89 fields = {} 90 90 for i in range(len(self.cursor.description)) : 91 91 fields[i] = self.cursor.description[i][0] 92 for row in result : 92 for row in result : 93 93 rowdict = {} 94 94 for field in fields.keys() : … … 99 99 pass 100 100 rowdict[fields[field]] = value 101 rows.append(rowdict) 102 return rows 103 101 rows.append(rowdict) 102 return rows 103 104 104 def doModify(self, query) : 105 105 """Does a (possibly multiple) modify query.""" 106 query = query.strip() 107 if not query.endswith(';') : 106 query = query.strip() 107 if not query.endswith(';') : 108 108 query += ';' 109 109 try : 110 110 self.querydebug("QUERY : %s" % query) 111 111 self.cursor.execute(query) 112 except self.database.Error, msg : 112 except self.database.Error, msg : 113 113 self.tool.logdebug("Query failed : %s" % repr(msg)) 114 114 raise PyKotaStorageError, repr(msg) 115 115 116 116 def doQuote(self, field) : 117 117 """Quotes a field for use as a string in SQL queries.""" 118 if type(field) == type(0.0) : 118 if type(field) == type(0.0) : 119 119 return field 120 elif type(field) == type(0) : 120 elif type(field) == type(0) : 121 121 return field 122 elif type(field) == type(0L) : 122 elif type(field) == type(0L) : 123 123 return field 124 124 elif field is not None : 125 125 return ("'%s'" % field.replace("'", "''")).decode("UTF-8") 126 else : 126 else : 127 127 return "NULL" 128 128 129 129 def prepareRawResult(self, result) : 130 130 """Prepares a raw result by including the headers.""" 131 131 if result : 132 132 entries = [tuple([f[0] for f in self.cursor.description])] 133 for entry in result : 133 for entry in result : 134 134 row = [] 135 135 for value in entry : … … 139 139 pass 140 140 row.append(value) 141 entries.append(tuple(row)) 142 return entries 143 141 entries.append(tuple(row)) 142 return entries 143 -
pykota/trunk/pykota/storages/sql.py
r3411 r3413 8 8 # the Free Software Foundation, either version 3 of the License, or 9 9 # (at your option) any later version. 10 # 10 # 11 11 # This program is distributed in the hope that it will be useful, 12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 14 # GNU General Public License for more details. 15 # 15 # 16 16 # You should have received a copy of the GNU General Public License 17 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. … … 26 26 StorageJob, StorageLastJob, StorageUserPQuota, \ 27 27 StorageGroupPQuota, StorageBillingCode 28 29 from pykota.utils import * 28 29 from pykota.utils import * 30 30 31 31 class SQLStorage : … … 42 42 user.Exists = True 43 43 return user 44 44 45 45 def storageGroupFromRecord(self, groupname, record) : 46 46 """Returns a StorageGroup instance from a database record.""" … … 53 53 group.Exists = True 54 54 return group 55 55 56 56 def storagePrinterFromRecord(self, printername, record) : 57 57 """Returns a StoragePrinter instance from a database record.""" … … 69 69 printer.Exists = True 70 70 return printer 71 72 def setJobAttributesFromRecord(self, job, record) : 71 72 def setJobAttributesFromRecord(self, job, record) : 73 73 """Sets the attributes of a job from a database record.""" 74 74 job.ident = record.get("id") … … 78 78 job.JobPrice = record.get("jobprice") 79 79 job.JobAction = record.get("action") 80 job.JobFileName = databaseToUnicode(record.get("filename") or "") 81 job.JobTitle = databaseToUnicode(record.get("title") or "") 80 job.JobFileName = databaseToUnicode(record.get("filename") or "") 81 job.JobTitle = databaseToUnicode(record.get("title") or "") 82 82 job.JobCopies = record.get("copies") 83 job.JobOptions = databaseToUnicode(record.get("options") or "") 83 job.JobOptions = databaseToUnicode(record.get("options") or "") 84 84 job.JobDate = record.get("jobdate") 85 85 job.JobHostName = record.get("hostname") … … 95 95 (job.JobTitle, job.JobFileName, job.JobOptions) = (_("Hidden because of privacy concerns"),) * 3 96 96 job.Exists = True 97 97 98 98 def storageJobFromRecord(self, record) : 99 99 """Returns a StorageJob instance from a database record.""" … … 101 101 self.setJobAttributesFromRecord(job, record) 102 102 return job 103 103 104 104 def storageLastJobFromRecord(self, printer, record) : 105 105 """Returns a StorageLastJob instance from a database record.""" … … 107 107 self.setJobAttributesFromRecord(lastjob, record) 108 108 return lastjob 109 109 110 110 def storageUserPQuotaFromRecord(self, user, printer, record) : 111 111 """Returns a StorageUserPQuota instance from a database record.""" … … 120 120 userpquota.Exists = True 121 121 return userpquota 122 122 123 123 def storageGroupPQuotaFromRecord(self, group, printer, record) : 124 124 """Returns a StorageGroupPQuota instance from a database record.""" … … 135 135 grouppquota.Exists = True 136 136 return grouppquota 137 137 138 138 def storageBillingCodeFromRecord(self, billingcode, record) : 139 139 """Returns a StorageBillingCode instance from a database record.""" … … 145 145 code.Exists = True 146 146 return code 147 148 def createFilter(self, only) : 147 148 def createFilter(self, only) : 149 149 """Returns the appropriate SQL filter.""" 150 150 if only : … … 152 152 for (k, v) in only.items() : 153 153 expressions.append("%s=%s" % (k, self.doQuote(unicodeToDatabase(v)))) 154 return " AND ".join(expressions) 155 return "" 156 157 def createOrderBy(self, default, ordering) : 154 return " AND ".join(expressions) 155 return "" 156 157 def createOrderBy(self, default, ordering) : 158 158 """Creates a suitable ORDER BY statement based on a list of fieldnames prefixed with '+' (ASC) or '-' (DESC).""" 159 159 statements = [] 160 160 if not ordering : 161 161 ordering = default 162 for field in ordering : 163 if field.startswith("-") : 162 for field in ordering : 163 if field.startswith("-") : 164 164 statements.append("%s DESC" % field[1:]) 165 165 elif field.startswith("+") : 166 166 statements.append("%s ASC" % field[1:]) 167 else : 167 else : 168 168 statements.append("%s ASC" % field) 169 return ", ".join(statements) 170 169 return ", ".join(statements) 170 171 171 def extractPrinters(self, extractonly={}, ordering=[]) : 172 172 """Extracts all printer records.""" … … 177 177 result = self.doRawSearch("SELECT * FROM printers %(thefilter)s ORDER BY %(orderby)s" % locals()) 178 178 return self.prepareRawResult(result) 179 179 180 180 def extractUsers(self, extractonly={}, ordering=[]) : 181 181 """Extracts all user records.""" … … 186 186 result = self.doRawSearch("SELECT * FROM users %(thefilter)s ORDER BY %(orderby)s" % locals()) 187 187 return self.prepareRawResult(result) 188 188 189 189 def extractBillingcodes(self, extractonly={}, ordering=[]) : 190 190 """Extracts all billing codes records.""" … … 195 195 result = self.doRawSearch("SELECT * FROM billingcodes %(thefilter)s ORDER BY %(orderby)s" % locals()) 196 196 return self.prepareRawResult(result) 197 197 198 198 def extractGroups(self, extractonly={}, ordering=[]) : 199 199 """Extracts all group records.""" … … 204 204 result = self.doRawSearch("SELECT groups.*,COALESCE(SUM(balance), 0) AS balance, COALESCE(SUM(lifetimepaid), 0) as lifetimepaid FROM groups LEFT OUTER JOIN users ON users.id IN (SELECT userid FROM groupsmembers WHERE groupid=groups.id) %(thefilter)s GROUP BY groups.id,groups.groupname,groups.limitby,groups.description ORDER BY %(orderby)s" % locals()) 205 205 return self.prepareRawResult(result) 206 206 207 207 def extractPayments(self, extractonly={}, ordering=[]) : 208 208 """Extracts all payment records.""" … … 212 212 try : 213 213 del extractonly[limit] 214 except KeyError : 214 except KeyError : 215 215 pass 216 216 thefilter = self.createFilter(extractonly) … … 218 218 thefilter = "AND %s" % thefilter 219 219 (startdate, enddate) = self.cleanDates(startdate, enddate) 220 if startdate : 220 if startdate : 221 221 thefilter = "%s AND date>=%s" % (thefilter, self.doQuote(startdate)) 222 if enddate : 222 if enddate : 223 223 thefilter = "%s AND date<=%s" % (thefilter, self.doQuote(enddate)) 224 224 orderby = self.createOrderBy(["+payments.id"], ordering) 225 225 result = self.doRawSearch("SELECT username,payments.* FROM users,payments WHERE users.id=payments.userid %(thefilter)s ORDER BY %(orderby)s" % locals()) 226 226 return self.prepareRawResult(result) 227 227 228 228 def extractUpquotas(self, extractonly={}, ordering=[]) : 229 229 """Extracts all userpquota records.""" … … 234 234 result = self.doRawSearch("SELECT users.username,printers.printername,userpquota.* FROM users,printers,userpquota WHERE users.id=userpquota.userid AND printers.id=userpquota.printerid %(thefilter)s ORDER BY %(orderby)s" % locals()) 235 235 return self.prepareRawResult(result) 236 236 237 237 def extractGpquotas(self, extractonly={}, ordering=[]) : 238 238 """Extracts all grouppquota records.""" … … 243 243 result = self.doRawSearch("SELECT groups.groupname,printers.printername,grouppquota.*,coalesce(sum(pagecounter), 0) AS pagecounter,coalesce(sum(lifepagecounter), 0) AS lifepagecounter FROM groups,printers,grouppquota,userpquota WHERE groups.id=grouppquota.groupid AND printers.id=grouppquota.printerid AND userpquota.printerid=grouppquota.printerid AND userpquota.userid IN (SELECT userid FROM groupsmembers WHERE groupsmembers.groupid=grouppquota.groupid) %(thefilter)s GROUP BY grouppquota.id,grouppquota.groupid,grouppquota.printerid,grouppquota.softlimit,grouppquota.hardlimit,grouppquota.datelimit,grouppquota.maxjobsize,groups.groupname,printers.printername ORDER BY %(orderby)s" % locals()) 244 244 return self.prepareRawResult(result) 245 245 246 246 def extractUmembers(self, extractonly={}, ordering=[]) : 247 247 """Extracts all user groups members.""" … … 252 252 result = self.doRawSearch("SELECT groups.groupname, users.username, groupsmembers.* FROM groups,users,groupsmembers WHERE users.id=groupsmembers.userid AND groups.id=groupsmembers.groupid %(thefilter)s ORDER BY %(orderby)s" % locals()) 253 253 return self.prepareRawResult(result) 254 254 255 255 def extractPmembers(self, extractonly={}, ordering=[]) : 256 256 """Extracts all printer groups members.""" … … 268 268 result = self.doRawSearch("SELECT p1.printername as pgroupname, p2.printername as printername, printergroupsmembers.* FROM printers p1, printers p2, printergroupsmembers WHERE p1.id=printergroupsmembers.groupid AND p2.id=printergroupsmembers.printerid %(thefilter)s ORDER BY %(orderby)s" % locals()) 269 269 return self.prepareRawResult(result) 270 270 271 271 def extractHistory(self, extractonly={}, ordering=[]) : 272 272 """Extracts all jobhistory records.""" … … 276 276 try : 277 277 del extractonly[limit] 278 except KeyError : 278 except KeyError : 279 279 pass 280 280 thefilter = self.createFilter(extractonly) … … 282 282 thefilter = "AND %s" % thefilter 283 283 (startdate, enddate) = self.cleanDates(startdate, enddate) 284 if startdate : 284 if startdate : 285 285 thefilter = "%s AND jobdate>=%s" % (thefilter, self.doQuote(startdate)) 286 if enddate : 286 if enddate : 287 287 thefilter = "%s AND jobdate<=%s" % (thefilter, self.doQuote(enddate)) 288 288 orderby = self.createOrderBy(["+jobhistory.id"], ordering) 289 289 result = self.doRawSearch("SELECT users.username,printers.printername,jobhistory.* FROM users,printers,jobhistory WHERE users.id=jobhistory.userid AND printers.id=jobhistory.printerid %(thefilter)s ORDER BY %(orderby)s" % locals()) 290 290 return self.prepareRawResult(result) 291 291 292 292 def filterNames(self, records, attribute, patterns=None) : 293 293 """Returns a list of 'attribute' from a list of records. 294 294 295 295 Logs any missing attribute. 296 """ 296 """ 297 297 result = [] 298 298 for record in records : … … 307 307 if self.tool.matchString(attrval, patterns) : 308 308 result.append(attrval) 309 else : 309 else : 310 310 result.append(attrval) 311 return result 312 313 def getAllBillingCodes(self, billingcode=None) : 311 return result 312 313 def getAllBillingCodes(self, billingcode=None) : 314 314 """Extracts all billing codes or only the billing codes matching the optional parameter.""" 315 315 result = self.doSearch("SELECT billingcode FROM billingcodes") 316 316 if result : 317 317 return self.filterNames(result, "billingcode", billingcode) 318 else : 318 else : 319 319 return [] 320 321 def getAllPrintersNames(self, printername=None) : 320 321 def getAllPrintersNames(self, printername=None) : 322 322 """Extracts all printer names or only the printers' names matching the optional parameter.""" 323 323 result = self.doSearch("SELECT printername FROM printers") 324 324 if result : 325 325 return self.filterNames(result, "printername", printername) 326 else : 326 else : 327 327 return [] 328 329 def getAllUsersNames(self, username=None) : 328 329 def getAllUsersNames(self, username=None) : 330 330 """Extracts all user names.""" 331 331 result = self.doSearch("SELECT username FROM users") 332 332 if result : 333 333 return self.filterNames(result, "username", username) 334 else : 334 else : 335 335 return [] 336 337 def getAllGroupsNames(self, groupname=None) : 336 337 def getAllGroupsNames(self, groupname=None) : 338 338 """Extracts all group names.""" 339 339 result = self.doSearch("SELECT groupname FROM groups") … … 342 342 else : 343 343 return [] 344 344 345 345 def getUserNbJobsFromHistory(self, user) : 346 346 """Returns the number of jobs the user has in history.""" … … 349 349 return result[0]["count"] 350 350 return 0 351 352 def getUserFromBackend(self, username) : 351 352 def getUserFromBackend(self, username) : 353 353 """Extracts user information given its name.""" 354 354 result = self.doSearch("SELECT * FROM users WHERE username=%s"\ … … 356 356 if result : 357 357 return self.storageUserFromRecord(username, result[0]) 358 else : 358 else : 359 359 return StorageUser(self, username) 360 361 def getGroupFromBackend(self, groupname) : 360 361 def getGroupFromBackend(self, groupname) : 362 362 """Extracts group information given its name.""" 363 363 result = self.doSearch("SELECT groups.*,COALESCE(SUM(balance), 0.0) AS balance, COALESCE(SUM(lifetimepaid), 0.0) AS lifetimepaid FROM groups LEFT OUTER JOIN users ON users.id IN (SELECT userid FROM groupsmembers WHERE groupid=groups.id) WHERE groupname=%s GROUP BY groups.id,groups.groupname,groups.limitby,groups.description" \ … … 365 365 if result : 366 366 return self.storageGroupFromRecord(groupname, result[0]) 367 else : 367 else : 368 368 return StorageGroup(self, groupname) 369 370 def getPrinterFromBackend(self, printername) : 369 370 def getPrinterFromBackend(self, printername) : 371 371 """Extracts printer information given its name.""" 372 372 result = self.doSearch("SELECT * FROM printers WHERE printername=%s" \ … … 374 374 if result : 375 375 return self.storagePrinterFromRecord(printername, result[0]) 376 else : 376 else : 377 377 return StoragePrinter(self, printername) 378 379 def getBillingCodeFromBackend(self, label) : 378 379 def getBillingCodeFromBackend(self, label) : 380 380 """Extracts a billing code information given its name.""" 381 381 result = self.doSearch("SELECT * FROM billingcodes WHERE billingcode=%s" \ … … 383 383 if result : 384 384 return self.storageBillingCodeFromRecord(label, result[0]) 385 else : 385 else : 386 386 return StorageBillingCode(self, label) 387 388 def getUserPQuotaFromBackend(self, user, printer) : 387 388 def getUserPQuotaFromBackend(self, user, printer) : 389 389 """Extracts a user print quota.""" 390 390 if printer.Exists and user.Exists : … … 394 394 return self.storageUserPQuotaFromRecord(user, printer, result[0]) 395 395 return StorageUserPQuota(self, user, printer) 396 397 def getGroupPQuotaFromBackend(self, group, printer) : 396 397 def getGroupPQuotaFromBackend(self, group, printer) : 398 398 """Extracts a group print quota.""" 399 399 if printer.Exists and group.Exists : … … 403 403 return self.storageGroupPQuotaFromRecord(group, printer, result[0]) 404 404 return StorageGroupPQuota(self, group, printer) 405 406 def getPrinterLastJobFromBackend(self, printer) : 405 406 def getPrinterLastJobFromBackend(self, printer) : 407 407 """Extracts a printer's last job information.""" 408 408 result = self.doSearch("SELECT jobhistory.id, jobid, userid, username, pagecounter, jobsize, jobprice, filename, title, copies, options, hostname, jobdate, md5sum, pages, billingcode, precomputedjobsize, precomputedjobprice FROM jobhistory, users WHERE userid=users.id AND jobhistory.id IN (SELECT max(id) FROM jobhistory WHERE printerid=%s)" % self.doQuote(printer.ident)) 409 409 if result : 410 410 return self.storageLastJobFromRecord(printer, result[0]) 411 else : 411 else : 412 412 return StorageLastJob(self, printer) 413 414 def getGroupMembersFromBackend(self, group) : 413 414 def getGroupMembersFromBackend(self, group) : 415 415 """Returns the group's members list.""" 416 416 groupmembers = [] … … 422 422 groupmembers.append(user) 423 423 self.cacheEntry("USERS", user.Name, user) 424 return groupmembers 425 426 def getUserGroupsFromBackend(self, user) : 424 return groupmembers 425 426 def getUserGroupsFromBackend(self, user) : 427 427 """Returns the user's groups list.""" 428 428 groups = [] … … 431 431 for record in result : 432 432 groups.append(self.getGroup(databaseToUnicode(record.get("groupname")))) 433 return groups 434 435 def getParentPrintersFromBackend(self, printer) : 433 return groups 434 435 def getParentPrintersFromBackend(self, printer) : 436 436 """Get all the printer groups this printer is a member of.""" 437 437 pgroups = [] … … 444 444 pgroups.append(parentprinter) 445 445 return pgroups 446 446 447 447 def getMatchingPrinters(self, printerpattern) : 448 448 """Returns the list of all printers for which name matches a certain pattern.""" … … 456 456 try : 457 457 patdict = {}.fromkeys(patterns) 458 except AttributeError : 458 except AttributeError : 459 459 # Python v2.2 or earlier 460 460 patdict = {} … … 467 467 printers.append(printer) 468 468 self.cacheEntry("PRINTERS", printer.Name, printer) 469 return printers 470 469 return printers 470 471 471 def getMatchingUsers(self, userpattern) : 472 472 """Returns the list of all users for which name matches a certain pattern.""" … … 480 480 try : 481 481 patdict = {}.fromkeys(patterns) 482 except AttributeError : 482 except AttributeError : 483 483 # Python v2.2 or earlier 484 484 patdict = {} … … 491 491 users.append(user) 492 492 self.cacheEntry("USERS", user.Name, user) 493 return users 494 493 return users 494 495 495 def getMatchingGroups(self, grouppattern) : 496 496 """Returns the list of all groups for which name matches a certain pattern.""" … … 504 504 try : 505 505 patdict = {}.fromkeys(patterns) 506 except AttributeError : 506 except AttributeError : 507 507 # Python v2.2 or earlier 508 508 patdict = {} … … 515 515 groups.append(group) 516 516 self.cacheEntry("GROUPS", group.Name, group) 517 return groups 518 517 return groups 518 519 519 def getMatchingBillingCodes(self, billingcodepattern) : 520 520 """Returns the list of all billing codes for which the label matches a certain pattern.""" … … 525 525 try : 526 526 patdict = {}.fromkeys(patterns) 527 except AttributeError : 527 except AttributeError : 528 528 # Python v2.2 or earlier 529 529 patdict = {} … … 536 536 codes.append(code) 537 537 self.cacheEntry("BILLINGCODES", code.BillingCode, code) 538 return codes 539 540 def getPrinterUsersAndQuotas(self, printer, names=["*"]) : 538 return codes 539 540 def getPrinterUsersAndQuotas(self, printer, names=["*"]) : 541 541 """Returns the list of users who uses a given printer, along with their quotas.""" 542 542 usersandquotas = [] … … 552 552 self.cacheEntry("USERPQUOTAS", "%s@%s" % (user.Name, printer.Name), userpquota) 553 553 return usersandquotas 554 555 def getPrinterGroupsAndQuotas(self, printer, names=["*"]) : 554 555 def getPrinterGroupsAndQuotas(self, printer, names=["*"]) : 556 556 """Returns the list of groups which uses a given printer, along with their quotas.""" 557 557 groupsandquotas = [] … … 565 565 groupsandquotas.append((group, grouppquota)) 566 566 return groupsandquotas 567 568 def addPrinter(self, printer) : 567 568 def addPrinter(self, printer) : 569 569 """Adds a printer to the quota storage, returns the old value if it already exists.""" 570 570 oldentry = self.getPrinter(printer.Name) … … 580 580 printer.isDirty = False 581 581 return None # the entry created doesn't need further modification 582 582 583 583 def addBillingCode(self, bcode) : 584 584 """Adds a billing code to the quota storage, returns the old value if it already exists.""" … … 587 587 return oldentry 588 588 self.doModify("INSERT INTO billingcodes (billingcode, balance, pagecounter, description) VALUES (%s, %s, %s, %s)" \ 589 % (self.doQuote(unicodeToDatabase(bcode.BillingCode)), 589 % (self.doQuote(unicodeToDatabase(bcode.BillingCode)), 590 590 self.doQuote(bcode.Balance or 0.0), \ 591 591 self.doQuote(bcode.PageCounter or 0), \ … … 593 593 bcode.isDirty = False 594 594 return None # the entry created doesn't need further modification 595 596 def addUser(self, user) : 595 596 def addUser(self, user) : 597 597 """Adds a user to the quota storage, returns the old value if it already exists.""" 598 598 oldentry = self.getUser(user.Name) … … 613 613 user.isDirty = False 614 614 return None # the entry created doesn't need further modification 615 616 def addGroup(self, group) : 615 616 def addGroup(self, group) : 617 617 """Adds a group to the quota storage, returns the old value if it already exists.""" 618 618 oldentry = self.getGroup(group.Name) … … 626 626 return None # the entry created doesn't need further modification 627 627 628 def addUserToGroup(self, user, group) : 628 def addUserToGroup(self, user, group) : 629 629 """Adds an user to a group.""" 630 630 result = self.doSearch("SELECT COUNT(*) AS mexists FROM groupsmembers WHERE groupid=%s AND userid=%s" % (self.doQuote(group.ident), self.doQuote(user.ident))) 631 631 try : 632 632 mexists = int(result[0].get("mexists")) 633 except (IndexError, TypeError) : 633 except (IndexError, TypeError) : 634 634 mexists = 0 635 if not mexists : 635 if not mexists : 636 636 self.doModify("INSERT INTO groupsmembers (groupid, userid) VALUES (%s, %s)" % (self.doQuote(group.ident), self.doQuote(user.ident))) 637 638 def delUserFromGroup(self, user, group) : 637 638 def delUserFromGroup(self, user, group) : 639 639 """Removes an user from a group.""" 640 640 self.doModify("DELETE FROM groupsmembers WHERE groupid=%s AND userid=%s" % \ 641 641 (self.doQuote(group.ident), self.doQuote(user.ident))) 642 642 643 643 def addUserPQuota(self, upq) : 644 644 """Initializes a user print quota on a printer.""" … … 658 658 upq.isDirty = False 659 659 return None # the entry created doesn't need further modification 660 660 661 661 def addGroupPQuota(self, gpq) : 662 662 """Initializes a group print quota on a printer.""" … … 673 673 gpq.isDirty = False 674 674 return None # the entry created doesn't need further modification 675 676 def savePrinter(self, printer) : 675 676 def savePrinter(self, printer) : 677 677 """Saves the printer to the database in a single operation.""" 678 678 self.doModify("UPDATE printers SET passthrough=%s, maxjobsize=%s, description=%s, priceperpage=%s, priceperjob=%s WHERE id=%s" \ … … 683 683 self.doQuote(printer.PricePerJob or 0.0), \ 684 684 self.doQuote(printer.ident))) 685 686 def saveUser(self, user) : 685 686 def saveUser(self, user) : 687 687 """Saves the user to the database in a single operation.""" 688 688 self.doModify("UPDATE users SET limitby=%s, balance=%s, lifetimepaid=%s, email=%s, overcharge=%s, description=%s WHERE id=%s" \ … … 694 694 self.doQuote(unicodeToDatabase(user.Description)), \ 695 695 self.doQuote(user.ident))) 696 697 def saveGroup(self, group) : 696 697 def saveGroup(self, group) : 698 698 """Saves the group to the database in a single operation.""" 699 699 self.doModify("UPDATE groups SET limitby=%s, description=%s WHERE id=%s" \ … … 701 701 self.doQuote(unicodeToDatabase(group.Description)), \ 702 702 self.doQuote(group.ident))) 703 704 def writeUserPQuotaDateLimit(self, userpquota, datelimit) : 703 704 def writeUserPQuotaDateLimit(self, userpquota, datelimit) : 705 705 """Sets the date limit permanently for a user print quota.""" 706 706 self.doModify("UPDATE userpquota SET datelimit=%s WHERE id=%s" % (self.doQuote(datelimit), self.doQuote(userpquota.ident))) 707 708 def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) : 707 708 def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) : 709 709 """Sets the date limit permanently for a group print quota.""" 710 710 self.doModify("UPDATE grouppquota SET datelimit=%s WHERE id=%s" % (self.doQuote(datelimit), self.doQuote(grouppquota.ident))) 711 712 def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) : 711 712 def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) : 713 713 """Increase page counters for a user print quota.""" 714 714 self.doModify("UPDATE userpquota SET pagecounter=pagecounter + %s,lifepagecounter=lifepagecounter + %s WHERE id=%s" % (self.doQuote(nbpages), self.doQuote(nbpages), self.doQuote(userpquota.ident))) 715 716 def saveBillingCode(self, bcode) : 715 716 def saveBillingCode(self, bcode) : 717 717 """Saves the billing code to the database.""" 718 718 self.doModify("UPDATE billingcodes SET balance=%s, pagecounter=%s, description=%s WHERE id=%s" \ … … 721 721 self.doQuote(unicodeToDatabase(bcode.Description)), \ 722 722 self.doQuote(bcode.ident))) 723 723 724 724 def consumeBillingCode(self, bcode, pagecounter, balance) : 725 725 """Consumes from a billing code.""" 726 726 self.doModify("UPDATE billingcodes SET balance=balance + %s, pagecounter=pagecounter + %s WHERE id=%s" % (self.doQuote(balance), self.doQuote(pagecounter), self.doQuote(bcode.ident))) 727 728 def refundJob(self, jobident) : 727 728 def refundJob(self, jobident) : 729 729 """Marks a job as refunded in the history.""" 730 730 self.doModify("UPDATE jobhistory SET action='REFUND' WHERE id=%s;" % self.doQuote(jobident)) 731 732 def decreaseUserAccountBalance(self, user, amount) : 731 732 def decreaseUserAccountBalance(self, user, amount) : 733 733 """Decreases user's account balance from an amount.""" 734 734 self.doModify("UPDATE users SET balance=balance - %s WHERE id=%s" % (self.doQuote(amount), self.doQuote(user.ident))) 735 735 736 736 def writeNewPayment(self, user, amount, comment="") : 737 737 """Adds a new payment to the payments history.""" 738 738 if user.ident is not None : 739 739 self.doModify("INSERT INTO payments (userid, amount, description) VALUES (%s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(amount), self.doQuote(unicodeToDatabase(comment)))) 740 else : 740 else : 741 741 self.doModify("INSERT INTO payments (userid, amount, description) VALUES ((SELECT id FROM users WHERE username=%s), %s, %s)" % (self.doQuote(unicodeToDatabase(user.Name)), self.doQuote(amount), self.doQuote(unicodeToDatabase(comment)))) 742 743 def writeLastJobSize(self, lastjob, jobsize, jobprice) : 742 743 def writeLastJobSize(self, lastjob, jobsize, jobprice) : 744 744 """Sets the last job's size permanently.""" 745 745 self.doModify("UPDATE jobhistory SET jobsize=%s, jobprice=%s WHERE id=%s" % (self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(lastjob.ident))) 746 746 747 747 def writeJobNew(self, printer, user, jobid, pagecounter, action, jobsize=None, jobprice=None, filename=None, title=None, copies=None, options=None, clienthost=None, jobsizebytes=None, jobmd5sum=None, jobpages=None, jobbilling=None, precomputedsize=None, precomputedprice=None) : 748 748 """Adds a job in a printer's history.""" 749 if self.privacy : 749 if self.privacy : 750 750 # For legal reasons, we want to hide the title, filename and options 751 751 title = filename = options = "hidden" … … 757 757 if jobsize is not None : 758 758 self.doModify("INSERT INTO jobhistory (userid, printerid, jobid, pagecounter, action, jobsize, jobprice, filename, title, copies, options, hostname, jobsizebytes, md5sum, pages, billingcode, precomputedjobsize, precomputedjobprice) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(printer.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(clienthost), self.doQuote(jobsizebytes), self.doQuote(jobmd5sum), self.doQuote(jobpages), self.doQuote(jobbilling), self.doQuote(precomputedsize), self.doQuote(precomputedprice))) 759 else : 759 else : 760 760 self.doModify("INSERT INTO jobhistory (userid, printerid, jobid, pagecounter, action, filename, title, copies, options, hostname, jobsizebytes, md5sum, pages, billingcode, precomputedjobsize, precomputedjobprice) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(printer.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(clienthost), self.doQuote(jobsizebytes), self.doQuote(jobmd5sum), self.doQuote(jobpages), self.doQuote(jobbilling), self.doQuote(precomputedsize), self.doQuote(precomputedprice))) 761 else : 761 else : 762 762 # here we explicitly want to reset jobsize to NULL if needed 763 763 self.doModify("UPDATE jobhistory SET userid=%s, jobid=%s, pagecounter=%s, action=%s, jobsize=%s, jobprice=%s, filename=%s, title=%s, copies=%s, options=%s, hostname=%s, jobsizebytes=%s, md5sum=%s, pages=%s, billingcode=%s, precomputedjobsize=%s, precomputedjobprice=%s, jobdate=now() WHERE id=%s" % (self.doQuote(user.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(clienthost), self.doQuote(jobsizebytes), self.doQuote(jobmd5sum), self.doQuote(jobpages), self.doQuote(jobbilling), self.doQuote(precomputedsize), self.doQuote(precomputedprice), self.doQuote(printer.LastJob.ident))) 764 764 765 765 def saveUserPQuota(self, userpquota) : 766 766 """Saves an user print quota entry.""" … … 774 774 self.doQuote(userpquota.MaxJobSize), \ 775 775 self.doQuote(userpquota.ident))) 776 776 777 777 def writeUserPQuotaWarnCount(self, userpquota, warncount) : 778 778 """Sets the warn counter value for a user quota.""" 779 779 self.doModify("UPDATE userpquota SET warncount=%s WHERE id=%s" % (self.doQuote(warncount), self.doQuote(userpquota.ident))) 780 780 781 781 def increaseUserPQuotaWarnCount(self, userpquota) : 782 782 """Increases the warn counter value for a user quota.""" 783 783 self.doModify("UPDATE userpquota SET warncount=warncount+1 WHERE id=%s" % self.doQuote(userpquota.ident)) 784 784 785 785 def saveGroupPQuota(self, grouppquota) : 786 786 """Saves a group print quota entry.""" … … 798 798 for record in result : 799 799 children.append(record.get("printerid")) # TODO : put this into the database integrity rules 800 if printer.ident not in children : 800 if printer.ident not in children : 801 801 self.doModify("INSERT INTO printergroupsmembers (groupid, printerid) VALUES (%s, %s)" % (self.doQuote(pgroup.ident), self.doQuote(printer.ident))) 802 802 803 803 def removePrinterFromGroup(self, pgroup, printer) : 804 804 """Removes a printer from a printer group.""" 805 805 self.doModify("DELETE FROM printergroupsmembers WHERE groupid=%s AND printerid=%s" % (self.doQuote(pgroup.ident), self.doQuote(printer.ident))) 806 806 807 807 def retrieveHistory(self, user=None, printer=None, hostname=None, billingcode=None, jobid=None, limit=100, start=None, end=None) : 808 808 """Retrieves all print jobs for user on printer (or all) between start and end date, limited to first 100 results.""" … … 813 813 if printer is not None : # printer.ident is None anyway if printer doesn't exist 814 814 where.append("printerid=%s" % self.doQuote(printer.ident)) 815 if hostname is not None : 815 if hostname is not None : 816 816 where.append("hostname=%s" % self.doQuote(hostname)) 817 if billingcode is not None : 817 if billingcode is not None : 818 818 where.append("billingcode=%s" % self.doQuote(unicodeToDatabase(billingcode))) 819 if jobid is not None : 819 if jobid is not None : 820 820 where.append("jobid=%s" % self.doQuote(jobid)) # TODO : jobid is text, so unicodeToDatabase(jobid) but do all of them as well. 821 if start is not None : 821 if start is not None : 822 822 where.append("jobdate>=%s" % self.doQuote(start)) 823 if end is not None : 823 if end is not None : 824 824 where.append("jobdate<=%s" % self.doQuote(end)) 825 if where : 825 if where : 826 826 query += " AND %s" % " AND ".join(where) 827 827 query += " ORDER BY jobhistory.id DESC" … … 829 829 # TODO : LIMIT is not supported under DB2. 830 830 # TODO : so we must use something like " FETCH FIRST %s ROWS ONLY" % self.doQuote(int(limit)) 831 query += " LIMIT %s" % self.doQuote(int(limit)) 832 jobs = [] 833 result = self.doSearch(query) 831 query += " LIMIT %s" % self.doQuote(int(limit)) 832 jobs = [] 833 result = self.doSearch(query) 834 834 if result : 835 835 for fields in result : … … 837 837 jobs.append(job) 838 838 return jobs 839 840 def deleteUser(self, user) : 839 840 def deleteUser(self, user) : 841 841 """Completely deletes an user from the database.""" 842 842 # TODO : What should we do if we delete the last person who used a given printer ? 843 843 # TODO : we can't reassign the last job to the previous one, because next user would be 844 844 # TODO : incorrectly charged (overcharged). 845 for q in [ 845 for q in [ 846 846 "DELETE FROM payments WHERE userid=%s" % self.doQuote(user.ident), 847 847 "DELETE FROM groupsmembers WHERE userid=%s" % self.doQuote(user.ident), … … 851 851 ] : 852 852 self.doModify(q) 853 854 def multipleQueriesInTransaction(self, queries) : 853 854 def multipleQueriesInTransaction(self, queries) : 855 855 """Does many modifications in a single transaction.""" 856 856 self.beginTransaction() … … 858 858 for q in queries : 859 859 self.doModify(q) 860 except : 860 except : 861 861 self.rollbackTransaction() 862 862 raise 863 else : 863 else : 864 864 self.commitTransaction() 865 866 def deleteManyBillingCodes(self, billingcodes) : 865 866 def deleteManyBillingCodes(self, billingcodes) : 867 867 """Deletes many billing codes.""" 868 868 codeids = ", ".join(["%s" % self.doQuote(b.ident) for b in billingcodes]) 869 869 if codeids : 870 self.multipleQueriesInTransaction([ 870 self.multipleQueriesInTransaction([ 871 871 "DELETE FROM billingcodes WHERE id IN (%s)" % codeids,]) 872 873 def deleteManyUsers(self, users) : 872 873 def deleteManyUsers(self, users) : 874 874 """Deletes many users.""" 875 875 userids = ", ".join(["%s" % self.doQuote(u.ident) for u in users]) 876 876 if userids : 877 self.multipleQueriesInTransaction([ 877 self.multipleQueriesInTransaction([ 878 878 "DELETE FROM payments WHERE userid IN (%s)" % userids, 879 879 "DELETE FROM groupsmembers WHERE userid IN (%s)" % userids, … … 881 881 "DELETE FROM userpquota WHERE userid IN (%s)" % userids, 882 882 "DELETE FROM users WHERE id IN (%s)" % userids,]) 883 884 def deleteManyGroups(self, groups) : 883 884 def deleteManyGroups(self, groups) : 885 885 """Deletes many groups.""" 886 886 groupids = ", ".join(["%s" % self.doQuote(g.ident) for g in groups]) 887 887 if groupids : 888 self.multipleQueriesInTransaction([ 888 self.multipleQueriesInTransaction([ 889 889 "DELETE FROM groupsmembers WHERE groupid IN (%s)" % groupids, 890 890 "DELETE FROM grouppquota WHERE groupid IN (%s)" % groupids, 891 891 "DELETE FROM groups WHERE id IN (%s)" % groupids,]) 892 892 893 893 def deleteManyPrinters(self, printers) : 894 894 """Deletes many printers.""" 895 895 printerids = ", ".join(["%s" % self.doQuote(p.ident) for p in printers]) 896 896 if printerids : 897 self.multipleQueriesInTransaction([ 897 self.multipleQueriesInTransaction([ 898 898 "DELETE FROM printergroupsmembers WHERE groupid IN (%s) OR printerid IN (%s)" % (printerids, printerids), 899 899 "DELETE FROM jobhistory WHERE printerid IN (%s)" % printerids, … … 901 901 "DELETE FROM userpquota WHERE printerid IN (%s)" % printerids, 902 902 "DELETE FROM printers WHERE id IN (%s)" % printerids,]) 903 904 def deleteManyUserPQuotas(self, printers, users) : 903 904 def deleteManyUserPQuotas(self, printers, users) : 905 905 """Deletes many user print quota entries.""" 906 906 printerids = ", ".join(["%s" % self.doQuote(p.ident) for p in printers]) 907 907 userids = ", ".join(["%s" % self.doQuote(u.ident) for u in users]) 908 908 if userids and printerids : 909 self.multipleQueriesInTransaction([ 909 self.multipleQueriesInTransaction([ 910 910 "DELETE FROM jobhistory WHERE userid IN (%s) AND printerid IN (%s)" \ 911 911 % (userids, printerids), 912 912 "DELETE FROM userpquota WHERE userid IN (%s) AND printerid IN (%s)" \ 913 913 % (userids, printerids),]) 914 914 915 915 def deleteManyGroupPQuotas(self, printers, groups) : 916 916 """Deletes many group print quota entries.""" … … 918 918 groupids = ", ".join(["%s" % self.doQuote(g.ident) for g in groups]) 919 919 if groupids and printerids : 920 self.multipleQueriesInTransaction([ 920 self.multipleQueriesInTransaction([ 921 921 "DELETE FROM grouppquota WHERE groupid IN (%s) AND printerid IN (%s)" \ 922 922 % (groupids, printerids),]) 923 924 def deleteUserPQuota(self, upquota) : 923 924 def deleteUserPQuota(self, upquota) : 925 925 """Completely deletes an user print quota entry from the database.""" 926 for q in [ 926 for q in [ 927 927 "DELETE FROM jobhistory WHERE userid=%s AND printerid=%s" \ 928 928 % (self.doQuote(upquota.User.ident), self.doQuote(upquota.Printer.ident)), … … 930 930 ] : 931 931 self.doModify(q) 932 933 def deleteGroupPQuota(self, gpquota) : 932 933 def deleteGroupPQuota(self, gpquota) : 934 934 """Completely deletes a group print quota entry from the database.""" 935 for q in [ 935 for q in [ 936 936 "DELETE FROM grouppquota WHERE id=%s" % self.doQuote(gpquota.ident), 937 937 ] : 938 938 self.doModify(q) 939 940 def deleteGroup(self, group) : 939 940 def deleteGroup(self, group) : 941 941 """Completely deletes a group from the database.""" 942 942 for q in [ … … 944 944 "DELETE FROM grouppquota WHERE groupid=%s" % self.doQuote(group.ident), 945 945 "DELETE FROM groups WHERE id=%s" % self.doQuote(group.ident), 946 ] : 946 ] : 947 947 self.doModify(q) 948 949 def deletePrinter(self, printer) : 948 949 def deletePrinter(self, printer) : 950 950 """Completely deletes a printer from the database.""" 951 for q in [ 951 for q in [ 952 952 "DELETE FROM printergroupsmembers WHERE groupid=%s OR printerid=%s" % (self.doQuote(printer.ident), self.doQuote(printer.ident)), 953 953 "DELETE FROM jobhistory WHERE printerid=%s" % self.doQuote(printer.ident), … … 957 957 ] : 958 958 self.doModify(q) 959 960 def deleteBillingCode(self, code) : 959 960 def deleteBillingCode(self, code) : 961 961 """Completely deletes a billing code from the database.""" 962 962 for q in [ 963 963 "DELETE FROM billingcodes WHERE id=%s" % self.doQuote(code.ident), 964 ] : 964 ] : 965 965 self.doModify(q) 966 966