Changeset 3522 for pykota/branches/1.26_fixes/pykota/storages/sql.py
- Timestamp:
- 04/15/10 01:27:45 (14 years ago)
- Files:
-
- 1 modified
Legend:
- Unmodified
- Added
- Removed
-
pykota/branches/1.26_fixes/pykota/storages/sql.py
r3393 r3522 14 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 15 # GNU General Public License for more details. 16 # 16 # 17 17 # You should have received a copy of the GNU General Public License 18 18 # along with this program; if not, write to the Free Software … … 28 28 StorageJob, StorageLastJob, StorageUserPQuota, \ 29 29 StorageGroupPQuota, StorageBillingCode 30 31 MAXINNAMES = 500 # Maximum number of non-patterns names to use in a single IN statement 30 32 31 33 class SQLStorage : … … 42 44 user.Exists = True 43 45 return user 44 46 45 47 def storageGroupFromRecord(self, groupname, record) : 46 48 """Returns a StorageGroup instance from a database record.""" … … 53 55 group.Exists = True 54 56 return group 55 57 56 58 def storagePrinterFromRecord(self, printername, record) : 57 59 """Returns a StoragePrinter instance from a database record.""" … … 69 71 printer.Exists = True 70 72 return printer 71 72 def setJobAttributesFromRecord(self, job, record) : 73 74 def setJobAttributesFromRecord(self, job, record) : 73 75 """Sets the attributes of a job from a database record.""" 74 76 job.ident = record.get("id") … … 78 80 job.JobPrice = record.get("jobprice") 79 81 job.JobAction = record.get("action") 80 job.JobFileName = self.databaseToUserCharset(record.get("filename") or "") 81 job.JobTitle = self.databaseToUserCharset(record.get("title") or "") 82 job.JobFileName = self.databaseToUserCharset(record.get("filename") or "") 83 job.JobTitle = self.databaseToUserCharset(record.get("title") or "") 82 84 job.JobCopies = record.get("copies") 83 job.JobOptions = self.databaseToUserCharset(record.get("options") or "") 85 job.JobOptions = self.databaseToUserCharset(record.get("options") or "") 84 86 job.JobDate = record.get("jobdate") 85 87 job.JobHostName = record.get("hostname") … … 95 97 (job.JobTitle, job.JobFileName, job.JobOptions) = (_("Hidden because of privacy concerns"),) * 3 96 98 job.Exists = True 97 99 98 100 def storageJobFromRecord(self, record) : 99 101 """Returns a StorageJob instance from a database record.""" … … 101 103 self.setJobAttributesFromRecord(job, record) 102 104 return job 103 105 104 106 def storageLastJobFromRecord(self, printer, record) : 105 107 """Returns a StorageLastJob instance from a database record.""" … … 107 109 self.setJobAttributesFromRecord(lastjob, record) 108 110 return lastjob 109 111 110 112 def storageUserPQuotaFromRecord(self, user, printer, record) : 111 113 """Returns a StorageUserPQuota instance from a database record.""" … … 120 122 userpquota.Exists = True 121 123 return userpquota 122 124 123 125 def storageGroupPQuotaFromRecord(self, group, printer, record) : 124 126 """Returns a StorageGroupPQuota instance from a database record.""" … … 135 137 grouppquota.Exists = True 136 138 return grouppquota 137 139 138 140 def storageBillingCodeFromRecord(self, billingcode, record) : 139 141 """Returns a StorageBillingCode instance from a database record.""" … … 145 147 code.Exists = True 146 148 return code 147 148 def createFilter(self, only) : 149 150 def createFilter(self, only) : 149 151 """Returns the appropriate SQL filter.""" 150 152 if only : … … 152 154 for (k, v) in only.items() : 153 155 expressions.append("%s=%s" % (k, self.doQuote(self.userCharsetToDatabase(v)))) 154 return " AND ".join(expressions) 155 return "" 156 157 def createOrderBy(self, default, ordering) : 156 return " AND ".join(expressions) 157 return "" 158 159 def createOrderBy(self, default, ordering) : 158 160 """Creates a suitable ORDER BY statement based on a list of fieldnames prefixed with '+' (ASC) or '-' (DESC).""" 159 161 statements = [] 160 162 if not ordering : 161 163 ordering = default 162 for field in ordering : 163 if field.startswith("-") : 164 for field in ordering : 165 if field.startswith("-") : 164 166 statements.append("%s DESC" % field[1:]) 165 167 elif field.startswith("+") : 166 168 statements.append("%s ASC" % field[1:]) 167 else : 169 else : 168 170 statements.append("%s ASC" % field) 169 return ", ".join(statements) 170 171 return ", ".join(statements) 172 171 173 def extractPrinters(self, extractonly={}, ordering=[]) : 172 174 """Extracts all printer records.""" … … 177 179 result = self.doRawSearch("SELECT * FROM printers %(thefilter)s ORDER BY %(orderby)s" % locals()) 178 180 return self.prepareRawResult(result) 179 181 180 182 def extractUsers(self, extractonly={}, ordering=[]) : 181 183 """Extracts all user records.""" … … 186 188 result = self.doRawSearch("SELECT * FROM users %(thefilter)s ORDER BY %(orderby)s" % locals()) 187 189 return self.prepareRawResult(result) 188 190 189 191 def extractBillingcodes(self, extractonly={}, ordering=[]) : 190 192 """Extracts all billing codes records.""" … … 195 197 result = self.doRawSearch("SELECT * FROM billingcodes %(thefilter)s ORDER BY %(orderby)s" % locals()) 196 198 return self.prepareRawResult(result) 197 199 198 200 def extractGroups(self, extractonly={}, ordering=[]) : 199 201 """Extracts all group records.""" … … 204 206 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 207 return self.prepareRawResult(result) 206 208 207 209 def extractPayments(self, extractonly={}, ordering=[]) : 208 210 """Extracts all payment records.""" … … 212 214 try : 213 215 del extractonly[limit] 214 except KeyError : 216 except KeyError : 215 217 pass 216 218 thefilter = self.createFilter(extractonly) … … 218 220 thefilter = "AND %s" % thefilter 219 221 (startdate, enddate) = self.cleanDates(startdate, enddate) 220 if startdate : 222 if startdate : 221 223 thefilter = "%s AND date>=%s" % (thefilter, self.doQuote(startdate)) 222 if enddate : 224 if enddate : 223 225 thefilter = "%s AND date<=%s" % (thefilter, self.doQuote(enddate)) 224 226 orderby = self.createOrderBy(["+payments.id"], ordering) 225 227 result = self.doRawSearch("SELECT username,payments.* FROM users,payments WHERE users.id=payments.userid %(thefilter)s ORDER BY %(orderby)s" % locals()) 226 228 return self.prepareRawResult(result) 227 229 228 230 def extractUpquotas(self, extractonly={}, ordering=[]) : 229 231 """Extracts all userpquota records.""" … … 234 236 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 237 return self.prepareRawResult(result) 236 238 237 239 def extractGpquotas(self, extractonly={}, ordering=[]) : 238 240 """Extracts all grouppquota records.""" … … 243 245 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 246 return self.prepareRawResult(result) 245 247 246 248 def extractUmembers(self, extractonly={}, ordering=[]) : 247 249 """Extracts all user groups members.""" … … 252 254 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 255 return self.prepareRawResult(result) 254 256 255 257 def extractPmembers(self, extractonly={}, ordering=[]) : 256 258 """Extracts all printer groups members.""" … … 268 270 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 271 return self.prepareRawResult(result) 270 272 271 273 def extractHistory(self, extractonly={}, ordering=[]) : 272 274 """Extracts all jobhistory records.""" … … 276 278 try : 277 279 del extractonly[limit] 278 except KeyError : 280 except KeyError : 279 281 pass 280 282 thefilter = self.createFilter(extractonly) … … 282 284 thefilter = "AND %s" % thefilter 283 285 (startdate, enddate) = self.cleanDates(startdate, enddate) 284 if startdate : 286 if startdate : 285 287 thefilter = "%s AND jobdate>=%s" % (thefilter, self.doQuote(startdate)) 286 if enddate : 288 if enddate : 287 289 thefilter = "%s AND jobdate<=%s" % (thefilter, self.doQuote(enddate)) 288 290 orderby = self.createOrderBy(["+jobhistory.id"], ordering) 289 291 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 292 return self.prepareRawResult(result) 291 293 292 294 def filterNames(self, records, attribute, patterns=None) : 293 295 """Returns a list of 'attribute' from a list of records. 294 296 295 297 Logs any missing attribute. 296 """ 298 """ 297 299 result = [] 298 300 for record in records : … … 307 309 if self.tool.matchString(attrval, patterns) : 308 310 result.append(attrval) 309 else : 311 else : 310 312 result.append(attrval) 311 return result 312 313 def getAllBillingCodes(self, billingcode=None) : 313 return result 314 315 def getAllBillingCodes(self, billingcode=None) : 314 316 """Extracts all billing codes or only the billing codes matching the optional parameter.""" 315 317 result = self.doSearch("SELECT billingcode FROM billingcodes") 316 318 if result : 317 319 return self.filterNames(result, "billingcode", billingcode) 318 else : 320 else : 319 321 return [] 320 321 def getAllPrintersNames(self, printername=None) : 322 323 def getAllPrintersNames(self, printername=None) : 322 324 """Extracts all printer names or only the printers' names matching the optional parameter.""" 323 325 result = self.doSearch("SELECT printername FROM printers") 324 326 if result : 325 327 return self.filterNames(result, "printername", printername) 326 else : 328 else : 327 329 return [] 328 329 def getAllUsersNames(self, username=None) : 330 331 def getAllUsersNames(self, username=None) : 330 332 """Extracts all user names.""" 331 333 result = self.doSearch("SELECT username FROM users") 332 334 if result : 333 335 return self.filterNames(result, "username", username) 334 else : 336 else : 335 337 return [] 336 337 def getAllGroupsNames(self, groupname=None) : 338 339 def getAllGroupsNames(self, groupname=None) : 338 340 """Extracts all group names.""" 339 341 result = self.doSearch("SELECT groupname FROM groups") … … 342 344 else : 343 345 return [] 344 346 345 347 def getUserNbJobsFromHistory(self, user) : 346 348 """Returns the number of jobs the user has in history.""" … … 349 351 return result[0]["count"] 350 352 return 0 351 352 def getUserFromBackend(self, username) : 353 354 def getUserFromBackend(self, username) : 353 355 """Extracts user information given its name.""" 354 356 result = self.doSearch("SELECT * FROM users WHERE username=%s LIMIT 1"\ … … 356 358 if result : 357 359 return self.storageUserFromRecord(username, result[0]) 358 else : 360 else : 359 361 return StorageUser(self, username) 360 361 def getGroupFromBackend(self, groupname) : 362 363 def getGroupFromBackend(self, groupname) : 362 364 """Extracts group information given its name.""" 363 365 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 LIMIT 1" \ … … 365 367 if result : 366 368 return self.storageGroupFromRecord(groupname, result[0]) 367 else : 369 else : 368 370 return StorageGroup(self, groupname) 369 370 def getPrinterFromBackend(self, printername) : 371 372 def getPrinterFromBackend(self, printername) : 371 373 """Extracts printer information given its name.""" 372 374 result = self.doSearch("SELECT * FROM printers WHERE printername=%s LIMIT 1" \ … … 374 376 if result : 375 377 return self.storagePrinterFromRecord(printername, result[0]) 376 else : 378 else : 377 379 return StoragePrinter(self, printername) 378 379 def getBillingCodeFromBackend(self, label) : 380 381 def getBillingCodeFromBackend(self, label) : 380 382 """Extracts a billing code information given its name.""" 381 383 result = self.doSearch("SELECT * FROM billingcodes WHERE billingcode=%s LIMIT 1" \ … … 383 385 if result : 384 386 return self.storageBillingCodeFromRecord(label, result[0]) 385 else : 387 else : 386 388 return StorageBillingCode(self, label) 387 388 def getUserPQuotaFromBackend(self, user, printer) : 389 390 def getUserPQuotaFromBackend(self, user, printer) : 389 391 """Extracts a user print quota.""" 390 392 if printer.Exists and user.Exists : … … 394 396 return self.storageUserPQuotaFromRecord(user, printer, result[0]) 395 397 return StorageUserPQuota(self, user, printer) 396 397 def getGroupPQuotaFromBackend(self, group, printer) : 398 399 def getGroupPQuotaFromBackend(self, group, printer) : 398 400 """Extracts a group print quota.""" 399 401 if printer.Exists and group.Exists : … … 403 405 return self.storageGroupPQuotaFromRecord(group, printer, result[0]) 404 406 return StorageGroupPQuota(self, group, printer) 405 406 def getPrinterLastJobFromBackend(self, printer) : 407 408 def getPrinterLastJobFromBackend(self, printer) : 407 409 """Extracts a printer's last job information.""" 408 410 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 printerid=%s AND userid=users.id ORDER BY jobdate DESC LIMIT 1" % self.doQuote(printer.ident)) 409 411 if result : 410 412 return self.storageLastJobFromRecord(printer, result[0]) 411 else : 413 else : 412 414 return StorageLastJob(self, printer) 413 414 def getGroupMembersFromBackend(self, group) : 415 416 def getGroupMembersFromBackend(self, group) : 415 417 """Returns the group's members list.""" 416 418 groupmembers = [] … … 422 424 groupmembers.append(user) 423 425 self.cacheEntry("USERS", user.Name, user) 424 return groupmembers 425 426 def getUserGroupsFromBackend(self, user) : 426 return groupmembers 427 428 def getUserGroupsFromBackend(self, user) : 427 429 """Returns the user's groups list.""" 428 430 groups = [] … … 431 433 for record in result : 432 434 groups.append(self.getGroup(self.databaseToUserCharset(record.get("groupname")))) 433 return groups 434 435 def getParentPrintersFromBackend(self, printer) : 435 return groups 436 437 def getParentPrintersFromBackend(self, printer) : 436 438 """Get all the printer groups this printer is a member of.""" 437 439 pgroups = [] … … 444 446 pgroups.append(parentprinter) 445 447 return pgroups 446 448 449 def hasWildCards(self, pattern) : 450 """Returns True if the pattern contains wildcards, else False.""" 451 specialchars = "*?[!" # no need to check for ] since [ would be there first 452 for specialchar in specialchars : 453 if specialchar in pattern : 454 return True 455 return False 456 447 457 def getMatchingPrinters(self, printerpattern) : 448 458 """Returns the list of all printers for which name matches a certain pattern.""" … … 454 464 if result : 455 465 patterns = printerpattern.split(",") 456 try : 457 patdict = {}.fromkeys(patterns) 458 except AttributeError : 459 # Python v2.2 or earlier 460 patdict = {} 461 for p in patterns : 462 patdict[p] = None 466 patdict = {}.fromkeys(patterns) 463 467 for record in result : 464 468 pname = self.databaseToUserCharset(record["printername"]) … … 467 471 printers.append(printer) 468 472 self.cacheEntry("PRINTERS", printer.Name, printer) 469 return printers 470 473 return printers 474 471 475 def getMatchingUsers(self, userpattern) : 472 476 """Returns the list of all users for which name matches a certain pattern.""" … … 475 479 # but we don't because other storages semantics may be different, so every 476 480 # storage should use fnmatch to match patterns and be storage agnostic 477 result = self.doSearch("SELECT * FROM users") 478 if result : 479 patterns = userpattern.split(",") 480 try : 481 patdict = {}.fromkeys(patterns) 482 except AttributeError : 483 # Python v2.2 or earlier 484 patdict = {} 485 for p in patterns : 486 patdict[p] = None 487 for record in result : 488 uname = self.databaseToUserCharset(record["username"]) 489 if patdict.has_key(uname) or self.tool.matchString(uname, patterns) : 490 user = self.storageUserFromRecord(uname, record) 491 users.append(user) 492 self.cacheEntry("USERS", user.Name, user) 493 return users 494 481 # 482 # This doesn't prevent us from being smarter, thanks to bse@chalmers.se 483 userpattern = userpattern or "*" 484 patterns = userpattern.split(",") 485 patdict = {}.fromkeys(patterns) 486 patterns = patdict.keys() # Ensures the uniqueness of each pattern, but we lose the cmd line ordering 487 # BEWARE : if a single pattern contains wild cards, we'll still use the slow route. 488 if self.hasWildCards(userpattern) : 489 # Slow route 490 result = self.doSearch("SELECT * FROM users") 491 if result : 492 for record in result : 493 uname = self.databaseToUserCharset(record["username"]) 494 if patdict.has_key(uname) or self.tool.matchString(uname, patterns) : 495 user = self.storageUserFromRecord(uname, record) 496 users.append(user) 497 self.cacheEntry("USERS", user.Name, user) 498 else : 499 # Fast route (probably not faster with a few users) 500 while patterns : 501 subset = patterns[:MAXINNAMES] 502 nbpatterns = len(subset) 503 if nbpatterns == 1 : 504 wherestmt = "username=%s" % self.doQuote(self.userCharsetToDatabase(subset[0])) 505 else : 506 wherestmt = "username IN (%s)" % ",".join([self.doQuote(self.userCharsetToDatabase(p)) for p in subset]) 507 result = self.doSearch("SELECT * FROM users WHERE %s" % wherestmt) 508 if result : 509 for record in result : 510 uname = self.databaseToUserCharset(record["username"]) 511 user = self.storageUserFromRecord(uname, record) 512 users.append(user) 513 self.cacheEntry("USERS", user.Name, user) 514 patterns = patterns[MAXINNAMES:] 515 users.sort(key=lambda u : u.Name) # Adds some ordering, we've already lost the cmd line one anyway. 516 return users 517 495 518 def getMatchingGroups(self, grouppattern) : 496 519 """Returns the list of all groups for which name matches a certain pattern.""" … … 502 525 if result : 503 526 patterns = grouppattern.split(",") 504 try : 505 patdict = {}.fromkeys(patterns) 506 except AttributeError : 507 # Python v2.2 or earlier 508 patdict = {} 509 for p in patterns : 510 patdict[p] = None 527 patdict = {}.fromkeys(patterns) 511 528 for record in result : 512 529 gname = self.databaseToUserCharset(record["groupname"]) … … 515 532 groups.append(group) 516 533 self.cacheEntry("GROUPS", group.Name, group) 517 return groups 518 534 return groups 535 519 536 def getMatchingBillingCodes(self, billingcodepattern) : 520 537 """Returns the list of all billing codes for which the label matches a certain pattern.""" … … 523 540 if result : 524 541 patterns = billingcodepattern.split(",") 525 try : 526 patdict = {}.fromkeys(patterns) 527 except AttributeError : 528 # Python v2.2 or earlier 529 patdict = {} 530 for p in patterns : 531 patdict[p] = None 542 patdict = {}.fromkeys(patterns) 532 543 for record in result : 533 544 codename = self.databaseToUserCharset(record["billingcode"]) … … 536 547 codes.append(code) 537 548 self.cacheEntry("BILLINGCODES", code.BillingCode, code) 538 return codes 539 540 def getPrinterUsersAndQuotas(self, printer, names=["*"]) : 549 return codes 550 551 def getPrinterUsersAndQuotas(self, printer, names=["*"]) : 541 552 """Returns the list of users who uses a given printer, along with their quotas.""" 542 553 usersandquotas = [] … … 552 563 self.cacheEntry("USERPQUOTAS", "%s@%s" % (user.Name, printer.Name), userpquota) 553 564 return usersandquotas 554 555 def getPrinterGroupsAndQuotas(self, printer, names=["*"]) : 565 566 def getPrinterGroupsAndQuotas(self, printer, names=["*"]) : 556 567 """Returns the list of groups which uses a given printer, along with their quotas.""" 557 568 groupsandquotas = [] … … 565 576 groupsandquotas.append((group, grouppquota)) 566 577 return groupsandquotas 567 568 def addPrinter(self, printer) : 578 579 def addPrinter(self, printer) : 569 580 """Adds a printer to the quota storage, returns the old value if it already exists.""" 570 581 oldentry = self.getPrinter(printer.Name) … … 580 591 printer.isDirty = False 581 592 return None # the entry created doesn't need further modification 582 593 583 594 def addBillingCode(self, bcode) : 584 595 """Adds a billing code to the quota storage, returns the old value if it already exists.""" … … 587 598 return oldentry 588 599 self.doModify("INSERT INTO billingcodes (billingcode, balance, pagecounter, description) VALUES (%s, %s, %s, %s)" \ 589 % (self.doQuote(self.userCharsetToDatabase(bcode.BillingCode)), 600 % (self.doQuote(self.userCharsetToDatabase(bcode.BillingCode)), 590 601 self.doQuote(bcode.Balance or 0.0), \ 591 602 self.doQuote(bcode.PageCounter or 0), \ … … 593 604 bcode.isDirty = False 594 605 return None # the entry created doesn't need further modification 595 596 def addUser(self, user) : 606 607 def addUser(self, user) : 597 608 """Adds a user to the quota storage, returns the old value if it already exists.""" 598 609 oldentry = self.getUser(user.Name) … … 613 624 user.isDirty = False 614 625 return None # the entry created doesn't need further modification 615 616 def addGroup(self, group) : 626 627 def addGroup(self, group) : 617 628 """Adds a group to the quota storage, returns the old value if it already exists.""" 618 629 oldentry = self.getGroup(group.Name) … … 626 637 return None # the entry created doesn't need further modification 627 638 628 def addUserToGroup(self, user, group) : 639 def addUserToGroup(self, user, group) : 629 640 """Adds an user to a group.""" 630 641 result = self.doSearch("SELECT COUNT(*) AS mexists FROM groupsmembers WHERE groupid=%s AND userid=%s" % (self.doQuote(group.ident), self.doQuote(user.ident))) 631 642 try : 632 643 mexists = int(result[0].get("mexists")) 633 except (IndexError, TypeError) : 644 except (IndexError, TypeError) : 634 645 mexists = 0 635 if not mexists : 646 if not mexists : 636 647 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) : 648 649 def delUserFromGroup(self, user, group) : 639 650 """Removes an user from a group.""" 640 651 self.doModify("DELETE FROM groupsmembers WHERE groupid=%s AND userid=%s" % \ 641 652 (self.doQuote(group.ident), self.doQuote(user.ident))) 642 653 643 654 def addUserPQuota(self, upq) : 644 655 """Initializes a user print quota on a printer.""" … … 658 669 upq.isDirty = False 659 670 return None # the entry created doesn't need further modification 660 671 661 672 def addGroupPQuota(self, gpq) : 662 673 """Initializes a group print quota on a printer.""" … … 673 684 gpq.isDirty = False 674 685 return None # the entry created doesn't need further modification 675 676 def savePrinter(self, printer) : 686 687 def savePrinter(self, printer) : 677 688 """Saves the printer to the database in a single operation.""" 678 689 self.doModify("UPDATE printers SET passthrough=%s, maxjobsize=%s, description=%s, priceperpage=%s, priceperjob=%s WHERE id=%s" \ … … 683 694 self.doQuote(printer.PricePerJob or 0.0), \ 684 695 self.doQuote(printer.ident))) 685 686 def saveUser(self, user) : 696 697 def saveUser(self, user) : 687 698 """Saves the user to the database in a single operation.""" 688 699 self.doModify("UPDATE users SET limitby=%s, balance=%s, lifetimepaid=%s, email=%s, overcharge=%s, description=%s WHERE id=%s" \ … … 694 705 self.doQuote(self.userCharsetToDatabase(user.Description)), \ 695 706 self.doQuote(user.ident))) 696 697 def saveGroup(self, group) : 707 708 def saveGroup(self, group) : 698 709 """Saves the group to the database in a single operation.""" 699 710 self.doModify("UPDATE groups SET limitby=%s, description=%s WHERE id=%s" \ … … 701 712 self.doQuote(self.userCharsetToDatabase(group.Description)), \ 702 713 self.doQuote(group.ident))) 703 704 def writeUserPQuotaDateLimit(self, userpquota, datelimit) : 714 715 def writeUserPQuotaDateLimit(self, userpquota, datelimit) : 705 716 """Sets the date limit permanently for a user print quota.""" 706 717 self.doModify("UPDATE userpquota SET datelimit=%s WHERE id=%s" % (self.doQuote(datelimit), self.doQuote(userpquota.ident))) 707 708 def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) : 718 719 def writeGroupPQuotaDateLimit(self, grouppquota, datelimit) : 709 720 """Sets the date limit permanently for a group print quota.""" 710 721 self.doModify("UPDATE grouppquota SET datelimit=%s WHERE id=%s" % (self.doQuote(datelimit), self.doQuote(grouppquota.ident))) 711 712 def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) : 722 723 def increaseUserPQuotaPagesCounters(self, userpquota, nbpages) : 713 724 """Increase page counters for a user print quota.""" 714 725 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) : 726 727 def saveBillingCode(self, bcode) : 717 728 """Saves the billing code to the database.""" 718 729 self.doModify("UPDATE billingcodes SET balance=%s, pagecounter=%s, description=%s WHERE id=%s" \ … … 721 732 self.doQuote(self.userCharsetToDatabase(bcode.Description)), \ 722 733 self.doQuote(bcode.ident))) 723 734 724 735 def consumeBillingCode(self, bcode, pagecounter, balance) : 725 736 """Consumes from a billing code.""" 726 737 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) : 738 739 def refundJob(self, jobident) : 729 740 """Marks a job as refunded in the history.""" 730 741 self.doModify("UPDATE jobhistory SET action='REFUND' WHERE id=%s;" % self.doQuote(jobident)) 731 732 def decreaseUserAccountBalance(self, user, amount) : 742 743 def decreaseUserAccountBalance(self, user, amount) : 733 744 """Decreases user's account balance from an amount.""" 734 745 self.doModify("UPDATE users SET balance=balance - %s WHERE id=%s" % (self.doQuote(amount), self.doQuote(user.ident))) 735 746 736 747 def writeNewPayment(self, user, amount, comment="") : 737 748 """Adds a new payment to the payments history.""" 738 749 if user.ident is not None : 739 750 self.doModify("INSERT INTO payments (userid, amount, description) VALUES (%s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(amount), self.doQuote(self.userCharsetToDatabase(comment)))) 740 else : 751 else : 741 752 self.doModify("INSERT INTO payments (userid, amount, description) VALUES ((SELECT id FROM users WHERE username=%s), %s, %s)" % (self.doQuote(self.userCharsetToDatabase(user.Name)), self.doQuote(amount), self.doQuote(self.userCharsetToDatabase(comment)))) 742 743 def writeLastJobSize(self, lastjob, jobsize, jobprice) : 753 754 def writeLastJobSize(self, lastjob, jobsize, jobprice) : 744 755 """Sets the last job's size permanently.""" 745 756 self.doModify("UPDATE jobhistory SET jobsize=%s, jobprice=%s WHERE id=%s" % (self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(lastjob.ident))) 746 757 747 758 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 759 """Adds a job in a printer's history.""" 749 if self.privacy : 760 if self.privacy : 750 761 # For legal reasons, we want to hide the title, filename and options 751 762 title = filename = options = "hidden" … … 757 768 if jobsize is not None : 758 769 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 : 770 else : 760 771 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 : 772 else : 762 773 # here we explicitly want to reset jobsize to NULL if needed 763 774 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 775 765 776 def saveUserPQuota(self, userpquota) : 766 777 """Saves an user print quota entry.""" … … 774 785 self.doQuote(userpquota.MaxJobSize), \ 775 786 self.doQuote(userpquota.ident))) 776 787 777 788 def writeUserPQuotaWarnCount(self, userpquota, warncount) : 778 789 """Sets the warn counter value for a user quota.""" 779 790 self.doModify("UPDATE userpquota SET warncount=%s WHERE id=%s" % (self.doQuote(warncount), self.doQuote(userpquota.ident))) 780 791 781 792 def increaseUserPQuotaWarnCount(self, userpquota) : 782 793 """Increases the warn counter value for a user quota.""" 783 794 self.doModify("UPDATE userpquota SET warncount=warncount+1 WHERE id=%s" % self.doQuote(userpquota.ident)) 784 795 785 796 def saveGroupPQuota(self, grouppquota) : 786 797 """Saves a group print quota entry.""" … … 798 809 for record in result : 799 810 children.append(record.get("printerid")) # TODO : put this into the database integrity rules 800 if printer.ident not in children : 811 if printer.ident not in children : 801 812 self.doModify("INSERT INTO printergroupsmembers (groupid, printerid) VALUES (%s, %s)" % (self.doQuote(pgroup.ident), self.doQuote(printer.ident))) 802 813 803 814 def removePrinterFromGroup(self, pgroup, printer) : 804 815 """Removes a printer from a printer group.""" 805 816 self.doModify("DELETE FROM printergroupsmembers WHERE groupid=%s AND printerid=%s" % (self.doQuote(pgroup.ident), self.doQuote(printer.ident))) 806 817 807 818 def retrieveHistory(self, user=None, printer=None, hostname=None, billingcode=None, jobid=None, limit=100, start=None, end=None) : 808 819 """Retrieves all print jobs for user on printer (or all) between start and end date, limited to first 100 results.""" … … 813 824 if printer is not None : # printer.ident is None anyway if printer doesn't exist 814 825 where.append("printerid=%s" % self.doQuote(printer.ident)) 815 if hostname is not None : 826 if hostname is not None : 816 827 where.append("hostname=%s" % self.doQuote(hostname)) 817 if billingcode is not None : 828 if billingcode is not None : 818 829 where.append("billingcode=%s" % self.doQuote(self.userCharsetToDatabase(billingcode))) 819 if jobid is not None : 830 if jobid is not None : 820 831 where.append("jobid=%s" % self.doQuote(jobid)) # TODO : jobid is text, so self.userCharsetToDatabase(jobid) but do all of them as well. 821 if start is not None : 832 if start is not None : 822 833 where.append("jobdate>=%s" % self.doQuote(start)) 823 if end is not None : 834 if end is not None : 824 835 where.append("jobdate<=%s" % self.doQuote(end)) 825 if where : 836 if where : 826 837 query += " AND %s" % " AND ".join(where) 827 838 query += " ORDER BY jobhistory.id DESC" 828 839 if limit : 829 840 query += " LIMIT %s" % self.doQuote(int(limit)) 830 jobs = [] 831 result = self.doSearch(query) 841 jobs = [] 842 result = self.doSearch(query) 832 843 if result : 833 844 for fields in result : … … 835 846 jobs.append(job) 836 847 return jobs 837 838 def deleteUser(self, user) : 848 849 def deleteUser(self, user) : 839 850 """Completely deletes an user from the database.""" 840 851 # TODO : What should we do if we delete the last person who used a given printer ? 841 852 # TODO : we can't reassign the last job to the previous one, because next user would be 842 853 # TODO : incorrectly charged (overcharged). 843 for q in [ 854 for q in [ 844 855 "DELETE FROM payments WHERE userid=%s" % self.doQuote(user.ident), 845 856 "DELETE FROM groupsmembers WHERE userid=%s" % self.doQuote(user.ident), … … 849 860 ] : 850 861 self.doModify(q) 851 852 def multipleQueriesInTransaction(self, queries) : 862 863 def multipleQueriesInTransaction(self, queries) : 853 864 """Does many modifications in a single transaction.""" 854 865 self.beginTransaction() … … 856 867 for q in queries : 857 868 self.doModify(q) 858 except : 869 except : 859 870 self.rollbackTransaction() 860 871 raise 861 else : 872 else : 862 873 self.commitTransaction() 863 864 def deleteManyBillingCodes(self, billingcodes) : 874 875 def deleteManyBillingCodes(self, billingcodes) : 865 876 """Deletes many billing codes.""" 866 877 codeids = ", ".join(["%s" % self.doQuote(b.ident) for b in billingcodes]) 867 878 if codeids : 868 self.multipleQueriesInTransaction([ 879 self.multipleQueriesInTransaction([ 869 880 "DELETE FROM billingcodes WHERE id IN (%s)" % codeids,]) 870 871 def deleteManyUsers(self, users) : 881 882 def deleteManyUsers(self, users) : 872 883 """Deletes many users.""" 873 884 userids = ", ".join(["%s" % self.doQuote(u.ident) for u in users]) 874 885 if userids : 875 self.multipleQueriesInTransaction([ 886 self.multipleQueriesInTransaction([ 876 887 "DELETE FROM payments WHERE userid IN (%s)" % userids, 877 888 "DELETE FROM groupsmembers WHERE userid IN (%s)" % userids, … … 879 890 "DELETE FROM userpquota WHERE userid IN (%s)" % userids, 880 891 "DELETE FROM users WHERE id IN (%s)" % userids,]) 881 882 def deleteManyGroups(self, groups) : 892 893 def deleteManyGroups(self, groups) : 883 894 """Deletes many groups.""" 884 895 groupids = ", ".join(["%s" % self.doQuote(g.ident) for g in groups]) 885 896 if groupids : 886 self.multipleQueriesInTransaction([ 897 self.multipleQueriesInTransaction([ 887 898 "DELETE FROM groupsmembers WHERE groupid IN (%s)" % groupids, 888 899 "DELETE FROM grouppquota WHERE groupid IN (%s)" % groupids, 889 900 "DELETE FROM groups WHERE id IN (%s)" % groupids,]) 890 901 891 902 def deleteManyPrinters(self, printers) : 892 903 """Deletes many printers.""" 893 904 printerids = ", ".join(["%s" % self.doQuote(p.ident) for p in printers]) 894 905 if printerids : 895 self.multipleQueriesInTransaction([ 906 self.multipleQueriesInTransaction([ 896 907 "DELETE FROM printergroupsmembers WHERE groupid IN (%s) OR printerid IN (%s)" % (printerids, printerids), 897 908 "DELETE FROM jobhistory WHERE printerid IN (%s)" % printerids, … … 899 910 "DELETE FROM userpquota WHERE printerid IN (%s)" % printerids, 900 911 "DELETE FROM printers WHERE id IN (%s)" % printerids,]) 901 902 def deleteManyUserPQuotas(self, printers, users) : 912 913 def deleteManyUserPQuotas(self, printers, users) : 903 914 """Deletes many user print quota entries.""" 904 915 printerids = ", ".join(["%s" % self.doQuote(p.ident) for p in printers]) 905 916 userids = ", ".join(["%s" % self.doQuote(u.ident) for u in users]) 906 917 if userids and printerids : 907 self.multipleQueriesInTransaction([ 918 self.multipleQueriesInTransaction([ 908 919 "DELETE FROM jobhistory WHERE userid IN (%s) AND printerid IN (%s)" \ 909 920 % (userids, printerids), 910 921 "DELETE FROM userpquota WHERE userid IN (%s) AND printerid IN (%s)" \ 911 922 % (userids, printerids),]) 912 923 913 924 def deleteManyGroupPQuotas(self, printers, groups) : 914 925 """Deletes many group print quota entries.""" … … 916 927 groupids = ", ".join(["%s" % self.doQuote(g.ident) for g in groups]) 917 928 if groupids and printerids : 918 self.multipleQueriesInTransaction([ 929 self.multipleQueriesInTransaction([ 919 930 "DELETE FROM grouppquota WHERE groupid IN (%s) AND printerid IN (%s)" \ 920 931 % (groupids, printerids),]) 921 922 def deleteUserPQuota(self, upquota) : 932 933 def deleteUserPQuota(self, upquota) : 923 934 """Completely deletes an user print quota entry from the database.""" 924 for q in [ 935 for q in [ 925 936 "DELETE FROM jobhistory WHERE userid=%s AND printerid=%s" \ 926 937 % (self.doQuote(upquota.User.ident), self.doQuote(upquota.Printer.ident)), … … 928 939 ] : 929 940 self.doModify(q) 930 931 def deleteGroupPQuota(self, gpquota) : 941 942 def deleteGroupPQuota(self, gpquota) : 932 943 """Completely deletes a group print quota entry from the database.""" 933 for q in [ 944 for q in [ 934 945 "DELETE FROM grouppquota WHERE id=%s" % self.doQuote(gpquota.ident), 935 946 ] : 936 947 self.doModify(q) 937 938 def deleteGroup(self, group) : 948 949 def deleteGroup(self, group) : 939 950 """Completely deletes a group from the database.""" 940 951 for q in [ … … 942 953 "DELETE FROM grouppquota WHERE groupid=%s" % self.doQuote(group.ident), 943 954 "DELETE FROM groups WHERE id=%s" % self.doQuote(group.ident), 944 ] : 955 ] : 945 956 self.doModify(q) 946 947 def deletePrinter(self, printer) : 957 958 def deletePrinter(self, printer) : 948 959 """Completely deletes a printer from the database.""" 949 for q in [ 960 for q in [ 950 961 "DELETE FROM printergroupsmembers WHERE groupid=%s OR printerid=%s" % (self.doQuote(printer.ident), self.doQuote(printer.ident)), 951 962 "DELETE FROM jobhistory WHERE printerid=%s" % self.doQuote(printer.ident), … … 955 966 ] : 956 967 self.doModify(q) 957 958 def deleteBillingCode(self, code) : 968 969 def deleteBillingCode(self, code) : 959 970 """Completely deletes a billing code from the database.""" 960 971 for q in [ 961 972 "DELETE FROM billingcodes WHERE id=%s" % self.doQuote(code.ident), 962 ] : 973 ] : 963 974 self.doModify(q) 964 975