Changeset 3444
- Timestamp:
- 10/26/08 20:39:19 (2 months ago)
- Files:
-
- 1 modified
-
pykota/branches/1.26_fixes/pykota/storage.py (modified) (50 diffs)
Legend:
- Unmodified
- Added
- Removed
-
pykota/branches/1.26_fixes/pykota/storage.py
r3133 r3444 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 … … 37 37 return self.message 38 38 __str__ = __repr__ 39 40 39 40 41 41 class StorageObject : 42 42 """Object present in the database.""" … … 48 48 self.isDirty = False 49 49 self.Exists = False 50 50 51 51 def setDescription(self, description=None) : 52 52 """Sets the object's description.""" 53 53 if description is not None : 54 54 self.Description = str(description) 55 self.isDirty = True 56 57 def save(self) : 55 self.isDirty = True 56 57 def save(self) : 58 58 """Saves the object to the database.""" 59 59 if self.isDirty : 60 60 getattr(self.parent, "save%s" % self.__class__.__name__[7:])(self) 61 61 self.isDirty = False 62 63 64 class StorageUser(StorageObject) : 62 63 64 class StorageUser(StorageObject) : 65 65 """User class.""" 66 66 def __init__(self, parent, name) : … … 74 74 self.Payments = [] # TODO : maybe handle this smartly for SQL, for now just don't retrieve them 75 75 self.PaymentsBacklog = [] 76 77 def consumeAccountBalance(self, amount) : 76 77 def consumeAccountBalance(self, amount) : 78 78 """Consumes an amount of money from the user's account balance.""" 79 79 self.parent.decreaseUserAccountBalance(self, amount) 80 80 self.AccountBalance = float(self.AccountBalance or 0.0) - amount 81 81 82 82 def setAccountBalance(self, balance, lifetimepaid, comment="") : 83 83 """Sets the user's account balance in case he pays more money.""" … … 88 88 self.PaymentsBacklog.append((diff, comment)) 89 89 self.isDirty = True 90 91 def save(self) : 90 91 def save(self) : 92 92 """Saves an user and flush its payments backlog.""" 93 93 for (value, comment) in self.PaymentsBacklog : 94 94 self.parent.writeNewPayment(self, value, comment) 95 self.PaymentsBacklog = [] 96 StorageObject.save(self) 97 98 def setLimitBy(self, limitby) : 95 self.PaymentsBacklog = [] 96 StorageObject.save(self) 97 98 def setLimitBy(self, limitby) : 99 99 """Sets the user's limiting factor.""" 100 100 try : 101 101 limitby = limitby.lower() 102 except AttributeError : 102 except AttributeError : 103 103 limitby = "quota" 104 104 if limitby in ["quota", "balance", \ … … 106 106 self.LimitBy = limitby 107 107 self.isDirty = True 108 109 def setOverChargeFactor(self, factor) : 108 109 def setOverChargeFactor(self, factor) : 110 110 """Sets the user's overcharging coefficient.""" 111 111 self.OverCharge = factor 112 112 self.isDirty = True 113 114 def setEmail(self, email) : 113 114 def setEmail(self, email) : 115 115 """Sets the user's email address.""" 116 116 self.Email = email 117 117 self.isDirty = True 118 119 def delete(self) : 118 119 def delete(self) : 120 120 """Deletes an user from the database.""" 121 121 self.parent.deleteUser(self) … … 126 126 self.parent.flushEntry("USERPQUOTAS", "%s@%s" % (v.User.Name, v.Printer.Name)) 127 127 self.Exists = False 128 self.isDirty = False 129 128 self.isDirty = False 129 130 130 def refund(self, amount, reason) : 131 131 """Refunds a number of credits to an user.""" 132 132 self.consumeAccountBalance(-amount) 133 133 self.parent.writeNewPayment(self, -amount, reason) 134 135 136 class StorageGroup(StorageObject) : 134 135 136 class StorageGroup(StorageObject) : 137 137 """User class.""" 138 138 def __init__(self, parent, name) : … … 142 142 self.AccountBalance = None 143 143 self.LifeTimePaid = None 144 145 def setLimitBy(self, limitby) : 144 145 def setLimitBy(self, limitby) : 146 146 """Sets the user's limiting factor.""" 147 147 try : 148 148 limitby = limitby.lower() 149 except AttributeError : 149 except AttributeError : 150 150 limitby = "quota" 151 151 if limitby in ["quota", "balance", "noquota"] : 152 152 self.LimitBy = limitby 153 153 self.isDirty = True 154 154 155 155 def addUserToGroup(self, user) : 156 156 """Adds an user to an users group.""" 157 157 self.parent.addUserToGroup(user, self) 158 158 159 159 def delUserFromGroup(self, user) : 160 160 """Removes an user from an users group.""" 161 161 self.parent.delUserFromGroup(user, self) 162 163 def delete(self) : 162 163 def delete(self) : 164 164 """Deletes a group from the database.""" 165 165 self.parent.deleteGroup(self) … … 170 170 self.parent.flushEntry("GROUPPQUOTAS", "%s@%s" % (v.Group.Name, v.Printer.Name)) 171 171 self.Exists = False 172 self.isDirty = False 173 174 172 self.isDirty = False 173 174 175 175 class StoragePrinter(StorageObject) : 176 176 """Printer class.""" … … 182 182 self.MaxJobSize = None 183 183 self.PassThrough = None 184 185 def __getattr__(self, name) : 184 185 def __getattr__(self, name) : 186 186 """Delays data retrieval until it's really needed.""" 187 if name == "LastJob" : 187 if name == "LastJob" : 188 188 self.LastJob = self.parent.getPrinterLastJob(self) 189 189 self.parent.tool.logdebug("Lazy retrieval of last job for printer %s" % self.Name) 190 190 return self.LastJob 191 elif name == "Coefficients" : 191 elif name == "Coefficients" : 192 192 self.Coefficients = self.parent.tool.config.getPrinterCoefficients(self.Name) 193 193 self.parent.tool.logdebug("Lazy retrieval of coefficients for printer %s : %s" % (self.Name, self.Coefficients)) … … 195 195 else : 196 196 raise AttributeError, name 197 197 198 198 def addJobToHistory(self, jobid, user, pagecounter, action, jobsize=None, jobprice=None, filename=None, title=None, copies=None, options=None, clienthost=None, jobsizebytes=None, jobmd5sum=None, jobpages=None, jobbilling=None, precomputedsize=None, precomputedprice=None) : 199 199 """Adds a job to the printer's history.""" 200 200 self.parent.writeJobNew(self, user, jobid, pagecounter, action, jobsize, jobprice, filename, title, copies, options, clienthost, jobsizebytes, jobmd5sum, jobpages, jobbilling, precomputedsize, precomputedprice) 201 201 # TODO : update LastJob object ? Probably not needed. 202 203 def addPrinterToGroup(self, printer) : 202 203 def addPrinterToGroup(self, printer) : 204 204 """Adds a printer to a printer group.""" 205 205 if (printer not in self.parent.getParentPrinters(self)) and (printer.ident != self.ident) : 206 206 self.parent.writePrinterToGroup(self, printer) 207 207 # TODO : reset cached value for printer parents, or add new parent to cached value 208 209 def delPrinterFromGroup(self, printer) : 208 209 def delPrinterFromGroup(self, printer) : 210 210 """Deletes a printer from a printer group.""" 211 211 self.parent.removePrinterFromGroup(self, printer) 212 212 # TODO : reset cached value for printer parents, or add new parent to cached value 213 214 def setPrices(self, priceperpage = None, priceperjob = None) : 213 214 def setPrices(self, priceperpage = None, priceperjob = None) : 215 215 """Sets the printer's prices.""" 216 216 if priceperpage is None : 217 217 priceperpage = self.PricePerPage or 0.0 218 else : 218 else : 219 219 self.PricePerPage = float(priceperpage) 220 if priceperjob is None : 220 if priceperjob is None : 221 221 priceperjob = self.PricePerJob or 0.0 222 else : 222 else : 223 223 self.PricePerJob = float(priceperjob) 224 self.isDirty = True 225 224 self.isDirty = True 225 226 226 def setPassThrough(self, passthrough) : 227 227 """Sets the printer's passthrough mode.""" 228 228 self.PassThrough = passthrough 229 229 self.isDirty = True 230 230 231 231 def setMaxJobSize(self, maxjobsize) : 232 232 """Sets the printer's maximal job size.""" 233 233 self.MaxJobSize = maxjobsize 234 234 self.isDirty = True 235 236 def delete(self) : 235 236 def delete(self) : 237 237 """Deletes a printer from the database.""" 238 238 self.parent.deletePrinter(self) … … 246 246 self.parent.flushEntry("GROUPPQUOTAS", "%s@%s" % (v.Group.Name, v.Printer.Name)) 247 247 self.Exists = False 248 self.isDirty = False 249 250 248 self.isDirty = False 249 250 251 251 class StorageUserPQuota(StorageObject) : 252 252 """User Print Quota class.""" … … 262 262 self.WarnCount = None 263 263 self.MaxJobSize = None 264 265 def __getattr__(self, name) : 264 265 def __getattr__(self, name) : 266 266 """Delays data retrieval until it's really needed.""" 267 if name == "ParentPrintersUserPQuota" : 267 if name == "ParentPrintersUserPQuota" : 268 268 self.ParentPrintersUserPQuota = (self.User.Exists and self.Printer.Exists and self.parent.getParentPrintersUserPQuota(self)) or [] 269 269 return self.ParentPrintersUserPQuota 270 270 else : 271 271 raise AttributeError, name 272 273 def setDateLimit(self, datelimit) : 272 273 def setDateLimit(self, datelimit) : 274 274 """Sets the date limit for this quota.""" 275 275 datelimit = DateTime.ISO.ParseDateTime(str(datelimit)[:19]) … … 277 277 self.parent.writeUserPQuotaDateLimit(self, date) 278 278 self.DateLimit = date 279 280 def setLimits(self, softlimit, hardlimit) : 279 280 def setLimits(self, softlimit, hardlimit) : 281 281 """Sets the soft and hard limit for this quota.""" 282 282 self.SoftLimit = softlimit … … 285 285 self.WarnCount = 0 286 286 self.isDirty = True 287 287 288 288 def setUsage(self, used) : 289 289 """Sets the PageCounter and LifePageCounter to used, or if used is + or - prefixed, changes the values of {Life,}PageCounter by that amount.""" … … 302 302 self.parent.increaseUserPQuotaWarnCount(self) 303 303 self.WarnCount = (self.WarnCount or 0) + 1 304 304 305 305 def resetDenyBannerCounter(self) : 306 306 """Resets the deny banner counter for this user quota.""" 307 307 self.parent.writeUserPQuotaWarnCount(self, 0) 308 308 self.WarnCount = 0 309 310 def reset(self) : 309 310 def reset(self) : 311 311 """Resets page counter to 0.""" 312 312 self.PageCounter = 0 313 313 self.DateLimit = None 314 314 self.isDirty = True 315 316 def hardreset(self) : 315 316 def hardreset(self) : 317 317 """Resets actual and life time page counters to 0.""" 318 318 self.PageCounter = self.LifePageCounter = 0 319 319 self.DateLimit = None 320 320 self.isDirty = True 321 322 def computeJobPrice(self, jobsize, inkusage=[]) : 321 322 def computeJobPrice(self, jobsize, inkusage=[]) : 323 323 """Computes the job price as the sum of all parent printers' prices + current printer's ones.""" 324 totalprice = 0.0 324 totalprice = 0.0 325 325 if jobsize : 326 326 if self.User.OverCharge != 0.0 : # optimization, but TODO : beware of rounding errors … … 330 330 if not inkusage : 331 331 totalprice += (jobsize * pageprice) 332 else : 332 else : 333 333 for pageindex in range(jobsize) : 334 334 try : 335 335 usage = inkusage[pageindex] 336 except IndexError : 336 except IndexError : 337 337 self.parent.tool.logdebug("No ink usage information. Using base cost of %f credits for page %i." % (pageprice, pageindex+1)) 338 338 totalprice += pageprice 339 else : 339 else : 340 340 coefficients = self.Printer.Coefficients 341 341 for (ink, value) in usage.items() : … … 346 346 totalprice += inkprice 347 347 if self.User.OverCharge != 1.0 : # TODO : beware of rounding errors 348 overcharged = totalprice * self.User.OverCharge 348 overcharged = totalprice * self.User.OverCharge 349 349 self.parent.tool.logdebug("Overcharging %s by a factor of %s ===> User %s will be charged for %s credits." % (totalprice, self.User.OverCharge, self.User.Name, overcharged)) 350 350 return overcharged 351 else : 351 else : 352 352 return totalprice 353 353 354 354 def increasePagesUsage(self, jobsize, inkusage=[]) : 355 355 """Increase the value of used pages and money.""" … … 363 363 upq.LifePageCounter = int(upq.LifePageCounter or 0) + jobsize 364 364 return jobprice 365 366 def delete(self) : 365 366 def delete(self) : 367 367 """Deletes an user print quota entry from the database.""" 368 368 self.parent.deleteUserPQuota(self) … … 371 371 self.Exists = False 372 372 self.isDirty = False 373 374 def refund(self, nbpages) : 373 374 def refund(self, nbpages) : 375 375 """Refunds a number of pages to an user on a particular printer.""" 376 376 self.parent.increaseUserPQuotaPagesCounters(self, -nbpages) 377 377 self.PageCounter = int(self.PageCounter or 0) - nbpages 378 378 self.LifePageCounter = int(self.LifePageCounter or 0) - nbpages 379 380 379 380 381 381 class StorageGroupPQuota(StorageObject) : 382 382 """Group Print Quota class.""" … … 391 391 self.DateLimit = None 392 392 self.MaxJobSize = None 393 394 def __getattr__(self, name) : 393 394 def __getattr__(self, name) : 395 395 """Delays data retrieval until it's really needed.""" 396 if name == "ParentPrintersGroupPQuota" : 396 if name == "ParentPrintersGroupPQuota" : 397 397 self.ParentPrintersGroupPQuota = (self.Group.Exists and self.Printer.Exists and self.parent.getParentPrintersGroupPQuota(self)) or [] 398 398 return self.ParentPrintersGroupPQuota 399 399 else : 400 400 raise AttributeError, name 401 402 def reset(self) : 401 402 def reset(self) : 403 403 """Resets page counter to 0.""" 404 404 for user in self.parent.getGroupMembers(self.Group) : … … 409 409 self.DateLimit = None 410 410 self.isDirty = True 411 412 def hardreset(self) : 411 412 def hardreset(self) : 413 413 """Resets actual and life time page counters to 0.""" 414 414 for user in self.parent.getGroupMembers(self.Group) : … … 419 419 self.DateLimit = None 420 420 self.isDirty = True 421 422 def setDateLimit(self, datelimit) : 421 422 def setDateLimit(self, datelimit) : 423 423 """Sets the date limit for this quota.""" 424 424 datelimit = DateTime.ISO.ParseDateTime(str(datelimit)[:19]) … … 431 431 self.parent.writeGroupPQuotaDateLimit(self, date) 432 432 self.DateLimit = date 433 434 def setLimits(self, softlimit, hardlimit) : 433 434 def setLimits(self, softlimit, hardlimit) : 435 435 """Sets the soft and hard limit for this quota.""" 436 436 self.SoftLimit = softlimit … … 438 438 self.DateLimit = None 439 439 self.isDirty = True 440 441 def delete(self) : 440 441 def delete(self) : 442 442 """Deletes a group print quota entry from the database.""" 443 443 self.parent.deleteGroupPQuota(self) … … 446 446 self.Exists = False 447 447 self.isDirty = False 448 449 448 449 450 450 class StorageJob(StorageObject) : 451 451 """Printer's Job class.""" … … 471 471 self.PrecomputedJobSize = None 472 472 self.PrecomputedJobPrice = None 473 474 def __getattr__(self, name) : 473 474 def __getattr__(self, name) : 475 475 """Delays data retrieval until it's really needed.""" 476 if name == "User" : 476 if name == "User" : 477 477 self.User = self.parent.getUser(self.UserName) 478 478 return self.User 479 elif name == "Printer" : 479 elif name == "Printer" : 480 480 self.Printer = self.parent.getPrinter(self.PrinterName) 481 481 return self.Printer 482 482 else : 483 483 raise AttributeError, name 484 485 def refund(self, reason) : 484 485 def refund(self, reason) : 486 486 """Refund a particular print job.""" 487 487 if (not self.JobSize) or (self.JobAction in ("DENY", "CANCEL", "REFUND")) : 488 488 return 489 489 try : 490 loginname = os.getlogin() 491 except OSError : 492 import pwd 493 loginname = pwd.getpwuid(os.getuid()).pw_name 490 494 basereason = _("Refunded %i pages and %.3f credits by %s (%s) on %s") \ 491 495 % (self.JobSize, 492 496 self.JobPrice, 493 497 self.parent.tool.originalUserName, 494 os.getlogin(),498 loginname, 495 499 str(DateTime.now())[:19]) 496 if reason : 500 if reason : 497 501 reason = "%s : %s" % (basereason, reason) 498 else : 502 else : 499 503 reason = basereason 500 self.parent.tool.logdebug("Refunding job %s..." % self.ident) 504 self.parent.tool.logdebug("Refunding job %s..." % self.ident) 501 505 self.parent.beginTransaction() 502 506 try : … … 504 508 bcode = self.parent.getBillingCode(self.JobBillingCode) 505 509 bcode.refund(self.JobSize, self.JobPrice) 506 510 507 511 if self.User.Exists : 508 512 self.User.refund(self.JobPrice, reason) 509 if self.Printer.Exists : 510 upq = self.parent.getUserPQuota(self.User, self.Printer) 513 if self.Printer.Exists : 514 upq = self.parent.getUserPQuota(self.User, self.Printer) 511 515 if upq.Exists : 512 516 upq.refund(self.JobSize) 513 517 self.parent.refundJob(self.ident) 514 except : 518 except : 515 519 self.parent.rollbackTransaction() 516 520 self.parent.tool.logdebug("Error while refunding job %s." % self.ident) 517 521 raise 518 else : 522 else : 519 523 self.parent.commitTransaction() 520 524 self.parent.tool.logdebug("Job %s refunded." % self.ident) 521 522 525 526 523 527 class StorageLastJob(StorageJob) : 524 528 """Printer's Last Job class.""" … … 527 531 self.PrinterName = printer.Name # not needed 528 532 self.Printer = printer 529 530 533 534 531 535 class StorageBillingCode(StorageObject) : 532 536 """Billing code class.""" … … 536 540 self.PageCounter = None 537 541 self.Balance = None 538 539 def delete(self) : 542 543 def delete(self) : 540 544 """Deletes the billing code from the database.""" 541 545 self.parent.deleteBillingCode(self) … … 543 547 self.isDirty = False 544 548 self.Exists = False 545 546 def reset(self, balance=0.0, pagecounter=0) : 549 550 def reset(self, balance=0.0, pagecounter=0) : 547 551 """Resets the pagecounter and balance for this billing code.""" 548 552 self.Balance = balance 549 553 self.PageCounter = pagecounter 550 554 self.isDirty = True 551 555 552 556 def consume(self, pages, price) : 553 557 """Consumes some pages and credits for this billing code.""" … … 556 560 self.PageCounter += pages 557 561 self.Balance -= price 558 562 559 563 def refund(self, pages, price) : 560 564 """Refunds a particular billing code.""" 561 565 self.consume(-pages, -price) 562 563 566 567 564 568 class BaseStorage : 565 569 def __init__(self, pykotatool) : … … 582 586 "LASTJOBS" : {}, \ 583 587 "BILLINGCODES" : {} } 584 585 def close(self) : 588 589 def close(self) : 586 590 """Must be overriden in children classes.""" 587 591 raise RuntimeError, "BaseStorage.close() must be overriden !" 588 589 def __del__(self) : 592 593 def __del__(self) : 590 594 """Ensures that the database connection is closed.""" 591 595 self.close() 592 596 593 597 def getFromCache(self, cachetype, key) :
