Changeset 2657
- Timestamp:
- 02/09/06 00:15:46 (19 years ago)
- Location:
- pykota/trunk
- Files:
-
- 6 modified
Legend:
- Unmodified
- Added
- Removed
-
pykota/trunk/bin/edpykota
r2655 r2657 253 253 suffix = (options["groups"] and "Group") or "User" 254 254 255 softlimit = hardlimit = None 256 257 limitby = options["limitby"] 258 if limitby : 259 limitby = limitby.strip().lower() 260 if limitby : 261 if limitby not in ('quota', 'balance', 'noquota', \ 262 'noprint', 'nochange') : 263 raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"] 264 if limitby in ('noquota', 'nochange') : 265 options["noquota"] = 1 266 if (limitby in ('nochange', 'noprint')) and options["groups"] : 267 raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"] 268 269 used = options["used"] 270 if used : 271 used = used.strip() 272 try : 273 int(used) 274 except ValueError : 275 raise PyKotaCommandLineError, _("Invalid used value %s.") % used 255 if options["delete"] : 256 if not names : 257 raise PyKotaCommandLineError, _("You have to pass user or group names on the command line") 258 self.display("Processing...\n") 259 todelete = getattr(self.storage, "getMatching%ss" % suffix)(",".join(names)) 260 nbtotal = len(todelete) 261 for i in range(nbtotal) : 262 todelete[i].delete() 263 percent = 100.0 * float(i) / float(nbtotal) 264 self.display("\r%.02f%%" % percent) 265 self.display("\r100.00%%\r ") 266 self.display("\r%s\n" % _("Done.")) 267 else : 268 softlimit = hardlimit = None 269 270 limitby = options["limitby"] 271 if limitby : 272 limitby = limitby.strip().lower() 273 if limitby : 274 if limitby not in ('quota', 'balance', 'noquota', \ 275 'noprint', 'nochange') : 276 raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"] 277 if limitby in ('noquota', 'nochange') : 278 options["noquota"] = 1 279 if (limitby in ('nochange', 'noprint')) and options["groups"] : 280 raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"] 281 282 used = options["used"] 283 if used : 284 used = used.strip() 285 try : 286 int(used) 287 except ValueError : 288 raise PyKotaCommandLineError, _("Invalid used value %s.") % used 289 290 increase = options["increase"] 291 if increase : 292 try : 293 increase = int(increase.strip()) 294 except ValueError : 295 raise PyKotaCommandLineError, _("Invalid increase value %s.") % increase 296 297 if not options["noquota"] : 298 if options["softlimit"] : 299 try : 300 softlimit = int(options["softlimit"].strip()) 301 if softlimit < 0 : 302 raise ValueError 303 except ValueError : 304 raise PyKotaCommandLineError, _("Invalid softlimit value %s.") % options["softlimit"] 305 if options["hardlimit"] : 306 try : 307 hardlimit = int(options["hardlimit"].strip()) 308 if hardlimit < 0 : 309 raise ValueError 310 except ValueError : 311 raise PyKotaCommandLineError, _("Invalid hardlimit value %s.") % options["hardlimit"] 312 if (softlimit is not None) and (hardlimit is not None) and (hardlimit < softlimit) : 313 # error, exchange them 314 self.printInfo(_("Hard limit %i is less than soft limit %i, values will be exchanged.") % (hardlimit, softlimit)) 315 (softlimit, hardlimit) = (hardlimit, softlimit) 276 316 277 increase = options["increase"] 278 if increase : 279 try : 280 increase = int(increase.strip()) 281 except ValueError : 282 raise PyKotaCommandLineError, _("Invalid increase value %s.") % increase 283 284 if not options["noquota"] : 285 if options["softlimit"] : 317 overcharge = options["overcharge"] 318 if overcharge : 286 319 try : 287 softlimit = int(options["softlimit"].strip()) 288 if softlimit < 0 : 289 raise ValueError 320 overcharge = float(overcharge.strip()) 321 except (ValueError, AttributeError) : 322 raise PyKotaCommandLineError, _("Invalid overcharge value %s") % options["overcharge"] 323 324 balance = options["balance"] 325 if balance : 326 balance = balance.strip() 327 try : 328 balancevalue = float(balance) 290 329 except ValueError : 291 raise PyKotaCommandLineError, _("Invalid softlimit value %s.") % options["softlimit"] 292 if options["hardlimit"] : 293 try : 294 hardlimit = int(options["hardlimit"].strip()) 295 if hardlimit < 0 : 296 raise ValueError 297 except ValueError : 298 raise PyKotaCommandLineError, _("Invalid hardlimit value %s.") % options["hardlimit"] 299 if (softlimit is not None) and (hardlimit is not None) and (hardlimit < softlimit) : 300 # error, exchange them 301 self.printInfo(_("Hard limit %i is less than soft limit %i, values will be exchanged.") % (hardlimit, softlimit)) 302 (softlimit, hardlimit) = (hardlimit, softlimit) 303 304 overcharge = options["overcharge"] 305 if overcharge : 306 try : 307 overcharge = float(overcharge.strip()) 308 except (ValueError, AttributeError) : 309 raise PyKotaCommandLineError, _("Invalid overcharge value %s") % options["overcharge"] 310 311 balance = options["balance"] 312 if balance : 313 balance = balance.strip() 314 try : 315 balancevalue = float(balance) 316 except ValueError : 317 raise PyKotaCommandLineError, _("Invalid balance value %s") % options["balance"] 318 319 if options["charge"] : 320 try : 321 charges = [float(part) for part in options["charge"].split(',', 1)] 322 except ValueError : 323 raise PyKotaCommandLineError, _("Invalid charge amount value %s") % options["charge"] 324 else : 325 if len(charges) > 2 : 326 charges = charges[:2] 327 if len(charges) != 2 : 328 charges = [charges[0], None] 329 330 if options["ingroups"] : 331 groupnames = [gname.strip() for gname in options["ingroups"].split(',')] 332 else : 333 groupnames = [] 334 335 rejectunknown = self.config.getRejectUnknown() 336 printeradded = 0 337 printers = self.storage.getMatchingPrinters(options["printer"]) 338 if not printers : 339 pname = options["printer"] 340 if options["add"] and pname : 341 if self.isValidName(pname) : 342 printers = [ self.storage.addPrinter(pname) ] 343 if printers[0].Exists : 344 printeradded = 1 345 else : 346 raise PyKotaToolError, _("Impossible to add printer %s") % pname 347 else : 348 raise PyKotaCommandLineError, _("Invalid printer name %s") % pname 349 else : 350 raise PyKotaCommandLineError, _("There's no printer matching %s") % pname 351 if not names : 352 if options["delete"] : 353 raise PyKotaCommandLineError, _("You have to pass user or group names on the command line") 354 else : 355 names = getattr(self.storage, "getAll%ssNames" % suffix)() # all users or groups 356 357 printersgroups = [] 358 if options["pgroups"] : 359 printersgroups = self.storage.getMatchingPrinters(options["pgroups"]) 360 361 if options["prototype"] : 362 protoentry = getattr(self.storage, "get%s" % suffix)(options["prototype"]) 363 if not protoentry.Exists : 364 raise PyKotaCommandLineError, _("Prototype object %s not found in Quota Storage.") % protoentry.Name 365 else : 366 limitby = protoentry.LimitBy 367 balancevalue = protoentry.AccountBalance 368 if balancevalue is not None : 369 balance = str(abs(balancevalue)) 370 else : 371 balance = None 372 overcharge = getattr(protoentry, "OverCharge", None) 373 374 sys.stdout.write(_("Managing print quotas... (this may take a lot of time)")) 375 sys.stdout.flush() 376 missingusers = {} 377 missinggroups = {} 378 todelete = {} 379 changed = {} # tracks changes made at the user/group level 380 for printer in printers : 381 for pgroup in printersgroups : 382 pgroup.addPrinterToGroup(printer) 330 raise PyKotaCommandLineError, _("Invalid balance value %s") % options["balance"] 383 331 384 332 if options["charge"] : 385 (perpage, perjob) = charges 386 printer.setPrices(perpage, perjob) 333 try : 334 charges = [float(part) for part in options["charge"].split(',', 1)] 335 except ValueError : 336 raise PyKotaCommandLineError, _("Invalid charge amount value %s") % options["charge"] 337 else : 338 if len(charges) > 2 : 339 charges = charges[:2] 340 if len(charges) != 2 : 341 charges = [charges[0], None] 342 343 if options["ingroups"] : 344 groupnames = [gname.strip() for gname in options["ingroups"].split(',')] 345 else : 346 groupnames = [] 387 347 388 if options["prototype"] : 389 protoquota = getattr(self.storage, "get%sPQuota" % suffix)(protoentry, printer) 390 if not protoquota.Exists : 391 self.printInfo(_("Prototype %s not found in Quota Storage for printer %s.") % (protoentry.Name, printer.Name)) 348 rejectunknown = self.config.getRejectUnknown() 349 printeradded = 0 350 printers = self.storage.getMatchingPrinters(options["printer"]) 351 if not printers : 352 pname = options["printer"] 353 if options["add"] and pname : 354 if self.isValidName(pname) : 355 printers = [ self.storage.addPrinter(pname) ] 356 if printers[0].Exists : 357 printeradded = 1 358 else : 359 raise PyKotaToolError, _("Impossible to add printer %s") % pname 360 else : 361 raise PyKotaCommandLineError, _("Invalid printer name %s") % pname 362 else : 363 raise PyKotaCommandLineError, _("There's no printer matching %s") % pname 364 if not names : 365 names = getattr(self.storage, "getAll%ssNames" % suffix)() # all users or groups 366 367 printersgroups = [] 368 if options["pgroups"] : 369 printersgroups = self.storage.getMatchingPrinters(options["pgroups"]) 370 371 if options["prototype"] : 372 protoentry = getattr(self.storage, "get%s" % suffix)(options["prototype"]) 373 if not protoentry.Exists : 374 raise PyKotaCommandLineError, _("Prototype object %s not found in Quota Storage.") % protoentry.Name 392 375 else : 393 (softlimit, hardlimit) = (protoquota.SoftLimit, protoquota.HardLimit) 376 limitby = protoentry.LimitBy 377 balancevalue = protoentry.AccountBalance 378 if balancevalue is not None : 379 balance = str(abs(balancevalue)) 380 else : 381 balance = None 382 overcharge = getattr(protoentry, "OverCharge", None) 383 384 sys.stdout.write(_("Managing print quotas... (this may take a lot of time)")) 385 sys.stdout.flush() 386 missingusers = {} 387 missinggroups = {} 388 changed = {} # tracks changes made at the user/group level 389 for printer in printers : 390 for pgroup in printersgroups : 391 pgroup.addPrinterToGroup(printer) 394 392 395 if not options["noquota"] : 396 if hardlimit is None : 397 hardlimit = softlimit 398 if hardlimit is not None : 399 self.printInfo(_("Undefined hard limit set to soft limit (%s) on printer %s.") % (str(hardlimit), printer.Name)) 400 if softlimit is None : 401 softlimit = hardlimit 402 if softlimit is not None : 403 self.printInfo(_("Undefined soft limit set to hard limit (%s) on printer %s.") % (str(softlimit), printer.Name)) 393 if options["charge"] : 394 (perpage, perjob) = charges 395 printer.setPrices(perpage, perjob) 396 397 if options["prototype"] : 398 protoquota = getattr(self.storage, "get%sPQuota" % suffix)(protoentry, printer) 399 if not protoquota.Exists : 400 self.printInfo(_("Prototype %s not found in Quota Storage for printer %s.") % (protoentry.Name, printer.Name)) 401 else : 402 (softlimit, hardlimit) = (protoquota.SoftLimit, protoquota.HardLimit) 404 403 405 if options["add"] : 406 allentries = [] 407 for name in names : 408 email = "" 409 if not options["groups"] : 410 splitname = name.split('/', 1) # username/email 411 if len(splitname) == 1 : 412 splitname.append("") 413 (name, email) = splitname 414 if email and (email.count('@') != 1) : 415 self.printInfo(_("Invalid email address %s") % email) 416 email = "" 417 entry = getattr(self.storage, "get%s" % suffix)(name) 418 if email and not options["groups"] : 419 entry.Email = email 420 entrypquota = getattr(self.storage, "get%sPQuota" % suffix)(entry, printer) 421 allentries.append((entry, entrypquota)) 422 else : 423 allentries = getattr(self.storage, "getPrinter%ssAndQuotas" % suffix)(printer, names) 424 425 # TODO : do this only once !!! 426 allnames = [entry.Name for (entry, dummy) in allentries] 427 for name in names : 428 if not self.matchString(name, allnames) : 429 if options["groups"] : 430 missinggroups[name] = 1 404 if not options["noquota"] : 405 if hardlimit is None : 406 hardlimit = softlimit 407 if hardlimit is not None : 408 self.printInfo(_("Undefined hard limit set to soft limit (%s) on printer %s.") % (str(hardlimit), printer.Name)) 409 if softlimit is None : 410 softlimit = hardlimit 411 if softlimit is not None : 412 self.printInfo(_("Undefined soft limit set to hard limit (%s) on printer %s.") % (str(softlimit), printer.Name)) 413 414 if options["add"] : 415 allentries = [] 416 for name in names : 417 email = "" 418 if not options["groups"] : 419 splitname = name.split('/', 1) # username/email 420 if len(splitname) == 1 : 421 splitname.append("") 422 (name, email) = splitname 423 if email and (email.count('@') != 1) : 424 self.printInfo(_("Invalid email address %s") % email) 425 email = "" 426 entry = getattr(self.storage, "get%s" % suffix)(name) 427 if email and not options["groups"] : 428 entry.Email = email 429 entrypquota = getattr(self.storage, "get%sPQuota" % suffix)(entry, printer) 430 allentries.append((entry, entrypquota)) 431 else : 432 allentries = getattr(self.storage, "getPrinter%ssAndQuotas" % suffix)(printer, names) 433 434 # TODO : do this only once !!! 435 allnames = [entry.Name for (entry, dummy) in allentries] 436 for name in names : 437 if not self.matchString(name, allnames) : 438 if options["groups"] : 439 missinggroups[name] = 1 440 else : 441 missingusers[name] = 1 442 443 for (entry, entrypquota) in allentries : 444 if not changed.has_key(entry.Name) : 445 changed[entry.Name] = {} 446 if not options["groups"] : 447 changed[entry.Name]["ingroups"] = [] 448 449 if not entry.Exists : 450 # not found 451 if options["add"] : 452 # In case we want to add something, it is crucial 453 # that we DON'T check with the system accounts files 454 # like /etc/passwd because users may be defined 455 # only remotely 456 if self.isValidName(entry.Name) : 457 reject = 0 458 if rejectunknown : 459 if options["groups"] : 460 try : 461 grp.getgrnam(entry.Name) 462 except KeyError : 463 self.printInfo(_("Unknown group %s") % entry.Name, "error") 464 reject = 1 465 else : 466 try : 467 pwd.getpwnam(entry.Name) 468 except KeyError : 469 self.printInfo(_("Unknown user %s") % entry.Name, "error") 470 reject = 1 471 if not reject : 472 entry = getattr(self.storage, "add%s" % suffix)(entry) 473 else : 474 if options["groups"] : 475 self.printInfo(_("Invalid group name %s") % entry.Name) 476 else : 477 self.printInfo(_("Invalid user name %s") % entry.Name) 478 else : 479 if options["groups"] : 480 missinggroups[entry.Name] = 1 481 else : 482 missingusers[entry.Name] = 1 483 484 if entry.Exists and (not entrypquota.Exists) : 485 # not found 486 if options["add"] : 487 entrypquota = getattr(self.storage, "add%sPQuota" % suffix)(entry, printer) 488 489 if not entrypquota.Exists : 490 self.printInfo(_("Quota not found for object %s on printer %s.") % (entry.Name, printer.Name)) 431 491 else : 432 missingusers[name] = 1 433 434 for (entry, entrypquota) in allentries : 435 if not changed.has_key(entry.Name) : 436 changed[entry.Name] = {} 437 if not options["groups"] : 438 changed[entry.Name]["ingroups"] = [] 492 if options["noquota"] or options["prototype"] \ 493 or ((softlimit is not None) and (hardlimit is not None)) : 494 entrypquota.setLimits(softlimit, hardlimit) 495 if increase : 496 if (entrypquota.SoftLimit is None) \ 497 or (entrypquota.HardLimit is None) : 498 self.printInfo(_("You can't increase limits by %s when no limit is set.") % increase, "error") 499 else : 500 newsoft = entrypquota.SoftLimit + increase 501 newhard = entrypquota.HardLimit + increase 502 if (newsoft >= 0) and (newhard >= 0) : 503 entrypquota.setLimits(newsoft, newhard) 504 else : 505 self.printInfo(_("You can't set negative limits."), "error") 506 if limitby : 507 if changed[entry.Name].get("limitby") is None : 508 entry.setLimitBy(limitby) 509 changed[entry.Name]["limitby"] = limitby 439 510 440 if not entry.Exists : 441 # not found 442 if options["add"] : 443 # In case we want to add something, it is crucial 444 # that we DON'T check with the system accounts files 445 # like /etc/passwd because users may be defined 446 # only remotely 447 if self.isValidName(entry.Name) : 448 reject = 0 449 if rejectunknown : 450 if options["groups"] : 451 try : 452 grp.getgrnam(entry.Name) 453 except KeyError : 454 self.printInfo(_("Unknown group %s") % entry.Name, "error") 455 reject = 1 456 else : 457 try : 458 pwd.getpwnam(entry.Name) 459 except KeyError : 460 self.printInfo(_("Unknown user %s") % entry.Name, "error") 461 reject = 1 462 if not reject : 463 entry = getattr(self.storage, "add%s" % suffix)(entry) 464 else : 465 if options["groups"] : 466 self.printInfo(_("Invalid group name %s") % entry.Name) 467 else : 468 self.printInfo(_("Invalid user name %s") % entry.Name) 469 else : 470 if options["groups"] : 471 missinggroups[entry.Name] = 1 472 else : 473 missingusers[entry.Name] = 1 474 elif options["delete"] : 475 todelete[entry.Name] = entry 511 if options["reset"] : 512 entrypquota.reset() 513 514 if options["hardreset"] : 515 entrypquota.hardreset() 516 517 if not options["groups"] : 518 if used : 519 entrypquota.setUsage(used) 476 520 477 if entry.Exists and (not entrypquota.Exists) : 478 # not found 479 if options["add"] : 480 entrypquota = getattr(self.storage, "add%sPQuota" % suffix)(entry, printer) 481 482 if not entrypquota.Exists : 483 self.printInfo(_("Quota not found for object %s on printer %s.") % (entry.Name, printer.Name)) 484 else : 485 if options["noquota"] or options["prototype"] \ 486 or ((softlimit is not None) and (hardlimit is not None)) : 487 entrypquota.setLimits(softlimit, hardlimit) 488 if increase : 489 if (entrypquota.SoftLimit is None) \ 490 or (entrypquota.HardLimit is None) : 491 self.printInfo(_("You can't increase limits by %s when no limit is set.") % increase, "error") 492 else : 493 newsoft = entrypquota.SoftLimit + increase 494 newhard = entrypquota.HardLimit + increase 495 if (newsoft >= 0) and (newhard >= 0) : 496 entrypquota.setLimits(newsoft, newhard) 497 else : 498 self.printInfo(_("You can't set negative limits."), "error") 499 if limitby : 500 if changed[entry.Name].get("limitby") is None : 501 entry.setLimitBy(limitby) 502 changed[entry.Name]["limitby"] = limitby 503 504 if options["reset"] : 505 entrypquota.reset() 506 507 if options["hardreset"] : 508 entrypquota.hardreset() 509 510 if not options["groups"] : 511 if used : 512 entrypquota.setUsage(used) 521 if overcharge is not None : 522 if changed[entry.Name].get("overcharge") is None : 523 entry.setOverChargeFactor(overcharge) 524 changed[entry.Name]["overcharge"] = overcharge 525 526 if balance : 527 if changed[entry.Name].get("balance") is None : 528 if balance.startswith("+") or balance.startswith("-") : 529 newbalance = float(entry.AccountBalance or 0.0) + balancevalue 530 newlifetimepaid = float(entry.LifeTimePaid or 0.0) + balancevalue 531 entry.setAccountBalance(newbalance, newlifetimepaid, options["comment"]) 532 else : 533 diff = balancevalue - float(entry.AccountBalance or 0.0) 534 newlifetimepaid = float(entry.LifeTimePaid or 0.0) + diff 535 entry.setAccountBalance(balancevalue, newlifetimepaid, options["comment"]) 536 changed[entry.Name]["balance"] = balance 537 538 for groupname in groupnames : 539 # not executed if option --ingroups is not used 540 if groupname not in changed[entry.Name]["ingroups"] : 541 group = self.storage.getGroup(groupname) 542 if group.Exists : 543 self.storage.addUserToGroup(entry, group) 544 changed[entry.Name]["ingroups"].append(groupname) 545 else : 546 self.printInfo(_("Group %s not found in the PyKota Storage.") % groupname) 513 547 514 if overcharge is not None : 515 if changed[entry.Name].get("overcharge") is None : 516 entry.setOverChargeFactor(overcharge) 517 changed[entry.Name]["overcharge"] = overcharge 518 519 if balance : 520 if changed[entry.Name].get("balance") is None : 521 if balance.startswith("+") or balance.startswith("-") : 522 newbalance = float(entry.AccountBalance or 0.0) + balancevalue 523 newlifetimepaid = float(entry.LifeTimePaid or 0.0) + balancevalue 524 entry.setAccountBalance(newbalance, newlifetimepaid, options["comment"]) 525 else : 526 diff = balancevalue - float(entry.AccountBalance or 0.0) 527 newlifetimepaid = float(entry.LifeTimePaid or 0.0) + diff 528 entry.setAccountBalance(balancevalue, newlifetimepaid, options["comment"]) 529 changed[entry.Name]["balance"] = balance 530 531 for groupname in groupnames : 532 # not executed if option --ingroups is not used 533 if groupname not in changed[entry.Name]["ingroups"] : 534 group = self.storage.getGroup(groupname) 535 if group.Exists : 536 self.storage.addUserToGroup(entry, group) 537 changed[entry.Name]["ingroups"].append(groupname) 538 else : 539 self.printInfo(_("Group %s not found in the PyKota Storage.") % groupname) 540 541 # Now outputs the list of nonexistent users and groups 542 for name in missingusers.keys() : 543 self.printInfo(_("Nonexistent user %s or missing print quota entry.") % name, level="warn") 544 for name in missinggroups.keys() : 545 self.printInfo(_("Nonexistent group %s or missing print quota entry.") % name, level="warn") 546 547 # Now delete what has to be deleted 548 for (name, entry) in todelete.items() : 549 entry.delete() 550 sys.stdout.write("\nDone.\n") 548 # Now outputs the list of nonexistent users and groups 549 for name in missingusers.keys() : 550 self.printInfo(_("Nonexistent user %s or missing print quota entry.") % name, level="warn") 551 for name in missinggroups.keys() : 552 self.printInfo(_("Nonexistent group %s or missing print quota entry.") % name, level="warn") 553 554 sys.stdout.write("\nDone.\n") 551 555 552 556 if __name__ == "__main__" : -
pykota/trunk/bin/pkbcodes
r2622 r2657 89 89 raise PyKotaCommandLineError, "%s : %s" % (pwd.getpwuid(os.geteuid())[0], _("You're not allowed to use this command.")) 90 90 91 if (options["list"] or options["reset"]) and not names : 92 names = ["*"] 93 94 if options["add"] : 95 billingcodes = [] 96 for bname in names : 97 billingcode = self.storage.getBillingCode(bname) 98 if billingcode.Exists : 99 if options["skipexisting"] : 100 self.printInfo(_("Billing code [%s] already exists, skipping.") % billingcode.BillingCode) 101 else : 102 self.printInfo(_("Billing code [%s] already exists, will be modified.") % billingcode.BillingCode) 103 billingcodes.append(billingcode) 104 else : 105 billingcode = self.storage.addBillingCode(bname) 106 if not billingcode.Exists : 107 raise PyKotaToolError, _("Impossible to add billingcode %s") % bname 108 else : 109 billingcodes.append(billingcode) 110 else : 111 billingcodes = self.storage.getMatchingBillingCodes(",".join(names)) 112 if not billingcodes : 113 raise PyKotaCommandLineError, _("There's no billingcode matching %s") % " ".join(names) 114 115 for billingcode in billingcodes : 116 if options["delete"] : 117 billingcode.delete() 118 elif options["list"] : 119 print "%s [%s] %s %s %s %.2f %s" % \ 120 (billingcode.BillingCode, billingcode.Description, \ 121 billingcode.PageCounter, \ 122 _("pages"), \ 123 _("and"), \ 124 billingcode.Balance, \ 125 _("credits")) 126 else : 127 if options["reset"] : 128 billingcode.reset() 129 if options["description"] is not None : 130 billingcode.setDescription(options["description"].strip()) 91 if options["delete"] : 92 self.display("Processing...\n") 93 todelete = self.storage.getMatchingBillingCodes(",".join(names)) 94 nbtotal = len(todelete) 95 for i in range(nbtotal) : 96 todelete[i].delete() 97 percent = 100.0 * float(i) / float(nbtotal) 98 self.display("\r%.02f%%" % percent) 99 self.display("\r100.00%%\r ") 100 self.display("\r%s\n" % _("Done.")) 101 else : 102 if (options["list"] or options["reset"]) and not names : 103 names = ["*"] 104 105 if options["add"] : 106 billingcodes = [] 107 for bname in names : 108 billingcode = self.storage.getBillingCode(bname) 109 if billingcode.Exists : 110 if options["skipexisting"] : 111 self.printInfo(_("Billing code [%s] already exists, skipping.") % billingcode.BillingCode) 112 else : 113 self.printInfo(_("Billing code [%s] already exists, will be modified.") % billingcode.BillingCode) 114 billingcodes.append(billingcode) 115 else : 116 billingcode = self.storage.addBillingCode(bname) 117 if not billingcode.Exists : 118 raise PyKotaToolError, _("Impossible to add billingcode %s") % bname 119 else : 120 billingcodes.append(billingcode) 121 else : 122 billingcodes = self.storage.getMatchingBillingCodes(",".join(names)) 123 if not billingcodes : 124 raise PyKotaCommandLineError, _("There's no billingcode matching %s") % " ".join(names) 125 126 for billingcode in billingcodes : 127 if options["list"] : 128 print "%s [%s] %s %s %s %.2f %s" % \ 129 (billingcode.BillingCode, billingcode.Description, \ 130 billingcode.PageCounter, \ 131 _("pages"), \ 132 _("and"), \ 133 billingcode.Balance, \ 134 _("credits")) 135 else : 136 if options["reset"] : 137 billingcode.reset() 138 if options["description"] is not None : 139 billingcode.setDescription(options["description"].strip()) 131 140 132 141 if __name__ == "__main__" : -
pykota/trunk/bin/pkprinters
r2622 r2657 129 129 raise PyKotaCommandLineError, "%s : %s" % (pwd.getpwuid(os.geteuid())[0], _("You're not allowed to use this command.")) 130 130 131 if options["list"] and not names : 132 names = ["*"] 133 134 if options["groups"] : 135 printersgroups = self.storage.getMatchingPrinters(options["groups"]) 136 if not printersgroups : 137 raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(options["groups"].split(',')) 138 139 if options["charge"] : 140 try : 141 charges = [float(part) for part in options["charge"].split(',', 1)] 142 except ValueError : 143 raise PyKotaCommandLineError, _("Invalid charge amount value %s") % options["charge"] 144 else : 145 if len(charges) > 2 : 146 charges = charges[:2] 147 if len(charges) != 2 : 148 charges = [charges[0], None] 149 (perpage, perjob) = charges 131 if options["delete"] : 132 self.display("Processing...\n") 133 todelete = self.storage.getMatchingPrinters(",".join(names)) 134 nbtotal = len(todelete) 135 for i in range(nbtotal) : 136 todelete[i].delete() 137 percent = 100.0 * float(i) / float(nbtotal) 138 self.display("\r%.02f%%" % percent) 139 self.display("\r100.00%%\r ") 140 self.display("\r%s\n" % _("Done.")) 141 else : 142 if options["list"] and not names : 143 names = ["*"] 150 144 151 if options["maxjobsize"] : 152 try : 153 maxjobsize = int(options["maxjobsize"]) 154 if maxjobsize < 0 : 155 raise ValueError 156 except ValueError : 157 raise PyKotaCommandLineError, _("Invalid maximum job size value %s") % options["maxjobsize"] 158 else : 159 maxjobsize = None 145 if options["groups"] : 146 printersgroups = self.storage.getMatchingPrinters(options["groups"]) 147 if not printersgroups : 148 raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(options["groups"].split(',')) 160 149 161 162 if options["add"] : 163 printers = [] 164 for pname in names : 165 printer = self.storage.getPrinter(pname) 166 if printer.Exists : 167 if options["skipexisting"] : 168 self.printInfo(_("Printer %s already exists, skipping.") % printer.Name) 169 else : 170 self.printInfo(_("Printer %s already exists, will be modified.") % printer.Name) 171 printers.append(printer) 172 else : 173 if self.isValidName(pname) : 174 printer = self.storage.addPrinter(pname) 175 if not printer.Exists : 176 raise PyKotaToolError, _("Impossible to add printer %s") % pname 150 if options["charge"] : 151 try : 152 charges = [float(part) for part in options["charge"].split(',', 1)] 153 except ValueError : 154 raise PyKotaCommandLineError, _("Invalid charge amount value %s") % options["charge"] 155 else : 156 if len(charges) > 2 : 157 charges = charges[:2] 158 if len(charges) != 2 : 159 charges = [charges[0], None] 160 (perpage, perjob) = charges 161 162 if options["maxjobsize"] : 163 try : 164 maxjobsize = int(options["maxjobsize"]) 165 if maxjobsize < 0 : 166 raise ValueError 167 except ValueError : 168 raise PyKotaCommandLineError, _("Invalid maximum job size value %s") % options["maxjobsize"] 169 else : 170 maxjobsize = None 171 172 173 if options["add"] : 174 printers = [] 175 for pname in names : 176 printer = self.storage.getPrinter(pname) 177 if printer.Exists : 178 if options["skipexisting"] : 179 self.printInfo(_("Printer %s already exists, skipping.") % printer.Name) 177 180 else : 181 self.printInfo(_("Printer %s already exists, will be modified.") % printer.Name) 178 182 printers.append(printer) 179 else : 180 raise PyKotaCommandLineError, _("Invalid printer name %s") % pname 181 else : 182 printers = self.storage.getMatchingPrinters(",".join(names)) 183 if not printers : 184 raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(names) 185 186 for printer in printers : 187 if options["delete"] : 188 printer.delete() 189 elif options["list"] : 190 parents = ", ".join([p.Name for p in self.storage.getParentPrinters(printer)]) 191 if parents : 192 parents = "%s %s" % (_("in"), parents) 193 print "%s [%s] (%s + #*%s)" % \ 194 (printer.Name, printer.Description, printer.PricePerJob, \ 195 printer.PricePerPage) 196 print " %s" % (_("Passthrough mode : %s") % ((printer.PassThrough and _("ON")) or _("OFF"))) 197 print " %s" % (_("Maximum job size : %s") % ((printer.MaxJobSize and (_("%s pages") % printer.MaxJobSize)) or _("Unlimited"))) 198 if parents : 199 print " %s" % parents 200 else : 201 if options["charge"] : 202 printer.setPrices(perpage, perjob) 203 if options["description"] is not None : 204 printer.setDescription(options["description"].strip()) 205 if options["nopassthrough"] and printer.PassThrough : 206 self.storage.setPrinterPassThroughMode(printer, 0) 207 if options["passthrough"] and not printer.PassThrough : 208 self.storage.setPrinterPassThroughMode(printer, 1) 209 if (maxjobsize is not None) and (printer.MaxJobSize != maxjobsize) : 210 self.storage.setPrinterMaxJobSize(printer, maxjobsize) 211 if options["groups"] : 212 for pgroup in printersgroups : 213 if options["remove"] : 214 pgroup.delPrinterFromGroup(printer) 215 else : 216 pgroup.addPrinterToGroup(printer) 183 else : 184 if self.isValidName(pname) : 185 printer = self.storage.addPrinter(pname) 186 if not printer.Exists : 187 raise PyKotaToolError, _("Impossible to add printer %s") % pname 188 else : 189 printers.append(printer) 190 else : 191 raise PyKotaCommandLineError, _("Invalid printer name %s") % pname 192 else : 193 printers = self.storage.getMatchingPrinters(",".join(names)) 194 if not printers : 195 raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(names) 196 197 for printer in printers : 198 if options["list"] : 199 parents = ", ".join([p.Name for p in self.storage.getParentPrinters(printer)]) 200 if parents : 201 parents = "%s %s" % (_("in"), parents) 202 print "%s [%s] (%s + #*%s)" % \ 203 (printer.Name, printer.Description, printer.PricePerJob, \ 204 printer.PricePerPage) 205 print " %s" % (_("Passthrough mode : %s") % ((printer.PassThrough and _("ON")) or _("OFF"))) 206 print " %s" % (_("Maximum job size : %s") % ((printer.MaxJobSize and (_("%s pages") % printer.MaxJobSize)) or _("Unlimited"))) 207 if parents : 208 print " %s" % parents 209 else : 210 if options["charge"] : 211 printer.setPrices(perpage, perjob) 212 if options["description"] is not None : 213 printer.setDescription(options["description"].strip()) 214 if options["nopassthrough"] and printer.PassThrough : 215 self.storage.setPrinterPassThroughMode(printer, 0) 216 if options["passthrough"] and not printer.PassThrough : 217 self.storage.setPrinterPassThroughMode(printer, 1) 218 if (maxjobsize is not None) and (printer.MaxJobSize != maxjobsize) : 219 self.storage.setPrinterMaxJobSize(printer, maxjobsize) 220 if options["groups"] : 221 for pgroup in printersgroups : 222 if options["remove"] : 223 pgroup.delPrinterFromGroup(printer) 224 else : 225 pgroup.addPrinterToGroup(printer) 217 226 218 227 if __name__ == "__main__" : -
pykota/trunk/pykota/storages/ldapstorage.py
r2653 r2657 357 357 fields = result[0][1] 358 358 user.ident = result[0][0] 359 user.Name = fields.get("pykotaUserName", [self.databaseToUserCharset(username)])[0]360 359 user.Email = fields.get(self.info["usermail"], [None])[0] 361 360 user.LimitBy = fields.get("pykotaLimitBy", ["quota"])[0] … … 670 669 printers = [] 671 670 # see comment at the same place in pgstorage.py 672 printerpattern = [self.userCharsetToDatabase(p) for p in printerpattern.split(",")] 673 result = self.doSearch("(&(objectClass=pykotaPrinter)(|%s))" % \ 674 "".join(["(pykotaPrinterName=%s)(%s=%s)" % (pname, self.info["printerrdn"], pname) for pname in printerpattern]), \ 671 result = self.doSearch("objectClass=pykotaPrinter", \ 675 672 ["pykotaPrinterName", "pykotaPricePerPage", "pykotaPricePerJob", "pykotaMaxJobSize", "pykotaPassThrough", "uniqueMember", "description"], \ 676 673 base=self.info["printerbase"]) 677 674 if result : 675 patterns = printerpattern.split(",") 678 676 for (printerid, fields) in result : 679 677 printername = self.databaseToUserCharset(fields.get("pykotaPrinterName", [""])[0] or fields.get(self.info["printerrdn"], [""])[0]) 680 printer = StoragePrinter(self, printername) 681 printer.ident = printerid 682 printer.PricePerJob = float(fields.get("pykotaPricePerJob", [0.0])[0] or 0.0) 683 printer.PricePerPage = float(fields.get("pykotaPricePerPage", [0.0])[0] or 0.0) 684 printer.MaxJobSize = int(fields.get("pykotaMaxJobSize", [0])[0]) 685 printer.PassThrough = fields.get("pykotaPassThrough", [None])[0] 686 if printer.PassThrough in (1, "1", "t", "true", "TRUE", "True") : 687 printer.PassThrough = 1 688 else : 689 printer.PassThrough = 0 690 printer.uniqueMember = fields.get("uniqueMember", []) 691 printer.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 692 printer.Exists = 1 693 printers.append(printer) 694 self.cacheEntry("PRINTERS", printer.Name, printer) 678 if self.tool.matchString(printername, patterns) : 679 printer = StoragePrinter(self, printername) 680 printer.ident = printerid 681 printer.PricePerJob = float(fields.get("pykotaPricePerJob", [0.0])[0] or 0.0) 682 printer.PricePerPage = float(fields.get("pykotaPricePerPage", [0.0])[0] or 0.0) 683 printer.MaxJobSize = int(fields.get("pykotaMaxJobSize", [0])[0]) 684 printer.PassThrough = fields.get("pykotaPassThrough", [None])[0] 685 if printer.PassThrough in (1, "1", "t", "true", "TRUE", "True") : 686 printer.PassThrough = 1 687 else : 688 printer.PassThrough = 0 689 printer.uniqueMember = fields.get("uniqueMember", []) 690 printer.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 691 printer.Exists = 1 692 printers.append(printer) 693 self.cacheEntry("PRINTERS", printer.Name, printer) 695 694 return printers 695 696 def getMatchingUsers(self, userpattern) : 697 """Returns the list of all users for which name matches a certain pattern.""" 698 users = [] 699 # see comment at the same place in pgstorage.py 700 result = self.doSearch("objectClass=pykotaAccount", \ 701 ["pykotaUserName", "pykotaLimitBy", self.info["usermail"], "pykotaOverCharge"], \ 702 base=self.info["userbase"]) 703 if result : 704 patterns = userpattern.split(",") 705 for (userid, fields) in result : 706 username = self.databaseToUserCharset(fields.get("pykotaUserName", [""])[0] or fields.get(self.info["userrdn"], [""])[0]) 707 if self.tool.matchString(username, patterns) : 708 user = StorageUser(self, username) 709 user.ident = userid 710 user.Email = fields.get(self.info["usermail"], [None])[0] 711 user.LimitBy = fields.get("pykotaLimitBy", ["quota"])[0] 712 user.OverCharge = float(fields.get("pykotaOverCharge", [1.0])[0]) 713 uname = self.userCharsetToDatabase(username) 714 result = self.doSearch("(&(objectClass=pykotaAccountBalance)(|(pykotaUserName=%s)(%s=%s)))" % \ 715 (uname, self.info["balancerdn"], uname), \ 716 ["pykotaBalance", "pykotaLifeTimePaid", "pykotaPayments"], \ 717 base=self.info["balancebase"]) 718 if not result : 719 raise PyKotaStorageError, _("No pykotaAccountBalance object found for user %s. Did you create LDAP entries manually ?") % username 720 else : 721 fields = result[0][1] 722 user.idbalance = result[0][0] 723 user.AccountBalance = fields.get("pykotaBalance") 724 if user.AccountBalance is not None : 725 if user.AccountBalance[0].upper() == "NONE" : 726 user.AccountBalance = None 727 else : 728 user.AccountBalance = float(user.AccountBalance[0]) 729 user.AccountBalance = user.AccountBalance or 0.0 730 user.LifeTimePaid = fields.get("pykotaLifeTimePaid") 731 if user.LifeTimePaid is not None : 732 if user.LifeTimePaid[0].upper() == "NONE" : 733 user.LifeTimePaid = None 734 else : 735 user.LifeTimePaid = float(user.LifeTimePaid[0]) 736 user.LifeTimePaid = user.LifeTimePaid or 0.0 737 user.Payments = [] 738 for payment in fields.get("pykotaPayments", []) : 739 try : 740 (date, amount, description) = payment.split(" # ") 741 except ValueError : 742 # Payment with no description (old Payment) 743 (date, amount) = payment.split(" # ") 744 description = "" 745 else : 746 description = self.databaseToUserCharset(base64.decodestring(description)) 747 user.Payments.append((date, float(amount), description)) 748 user.Exists = 1 749 users.append(user) 750 self.cacheEntry("USERS", user.Name, user) 751 return users 752 753 def getMatchingGroups(self, grouppattern) : 754 """Returns the list of all groups for which name matches a certain pattern.""" 755 groups = [] 756 # see comment at the same place in pgstorage.py 757 result = self.doSearch("objectClass=pykotaGroup", \ 758 ["pykotaGroupName", "pykotaLimitBy"], \ 759 base=self.info["groupbase"]) 760 if result : 761 patterns = grouppattern.split(",") 762 for (groupid, fields) in result : 763 groupname = self.databaseToUserCharset(fields.get("pykotaGroupName", [""])[0] or fields.get(self.info["grouprdn"], [""])[0]) 764 if self.tool.matchString(groupname, patterns) : 765 group = StorageGroup(self, groupname) 766 group.ident = groupid 767 group.Name = fields.get("pykotaGroupName", [self.databaseToUserCharset(groupname)])[0] 768 group.LimitBy = fields.get("pykotaLimitBy", ["quota"])[0] 769 group.AccountBalance = 0.0 770 group.LifeTimePaid = 0.0 771 for member in self.getGroupMembers(group) : 772 if member.Exists : 773 group.AccountBalance += member.AccountBalance 774 group.LifeTimePaid += member.LifeTimePaid 775 group.Exists = 1 776 return groups 696 777 697 778 def getPrinterUsersAndQuotas(self, printer, names=["*"]) : … … 1545 1626 """Returns the list of all billing codes which match a certain pattern.""" 1546 1627 codes = [] 1547 billingcodepattern = [self.userCharsetToDatabase(b) for b in billingcodepattern.split(",")] 1548 result = self.doSearch("(&(objectClass=pykotaBilling)(|%s))" % \ 1549 "".join(["(pykotaBillingCode=%s)" % bcode for bcode in billingcodepattern]), \ 1628 result = self.doSearch("objectClass=pykotaBilling", \ 1550 1629 ["pykotaBillingCode", "description", "pykotaPageCounter", "pykotaBalance"], \ 1551 1630 base=self.info["billingcodebase"]) 1552 1631 if result : 1632 patterns = billingcodepattern.split(",") 1553 1633 for (codeid, fields) in result : 1554 codename = self.databaseToUserCharset(fields.get("pykotaBillingCode", [""])[0]) 1555 code = StorageBillingCode(self, codename) 1556 code.ident = codeid 1557 code.BillingCode = codename 1558 code.PageCounter = int(fields.get("pykotaPageCounter", [0])[0]) 1559 code.Balance = float(fields.get("pykotaBalance", [0.0])[0]) 1560 code.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 1561 code.Exists = 1 1562 codes.append(code) 1563 self.cacheEntry("BILLINGCODES", code.BillingCode, code) 1634 bcode = self.databaseToUserCharset(fields.get("pykotaBillingCode", [""])[0]) 1635 if self.tool.matchString(bcode, patterns) : 1636 code = StorageBillingCode(self, codename) 1637 code.ident = codeid 1638 code.BillingCode = codename 1639 code.PageCounter = int(fields.get("pykotaPageCounter", [0])[0]) 1640 code.Balance = float(fields.get("pykotaBalance", [0.0])[0]) 1641 code.Description = self.databaseToUserCharset(fields.get("description", [""])[0]) 1642 code.Exists = 1 1643 codes.append(code) 1644 self.cacheEntry("BILLINGCODES", code.BillingCode, code) 1564 1645 return codes 1565 1646 -
pykota/trunk/pykota/storages/sql.py
r2654 r2657 203 203 fields = result[0] 204 204 user.ident = fields.get("id") 205 user.Name = self.databaseToUserCharset(fields.get("username", username))206 205 user.LimitBy = fields.get("limitby") or "quota" 207 206 user.AccountBalance = fields.get("balance") 208 207 user.LifeTimePaid = fields.get("lifetimepaid") 209 208 user.Email = fields.get("email") 209 user.Description = self.databaseToUserCharset(fields.get("description")) 210 210 user.OverCharge = fields.get("overcharge", 1.0) 211 211 user.Exists = 1 … … 220 220 fields = result[0] 221 221 group.ident = fields.get("id") 222 group.Name = self.databaseToUserCharset(fields.get("groupname", groupname))223 222 group.LimitBy = fields.get("limitby") or "quota" 224 223 group.AccountBalance = fields.get("balance") 225 224 group.LifeTimePaid = fields.get("lifetimepaid") 225 group.Description = self.databaseToUserCharset(fields.get("description")) 226 226 group.Exists = 1 227 227 return group … … 235 235 fields = result[0] 236 236 printer.ident = fields.get("id") 237 printer.Name = self.databaseToUserCharset(fields.get("printername", printername))238 237 printer.PricePerJob = fields.get("priceperjob") or 0.0 239 238 printer.PricePerPage = fields.get("priceperpage") or 0.0 … … 255 254 fields = result[0] 256 255 code.ident = fields.get("id") 257 code.BillingCode = self.databaseToUserCharset(fields.get("billingcode"))258 256 code.Description = self.databaseToUserCharset(fields.get("description") or "") 259 257 code.Balance = fields.get("balance") or 0.0 … … 375 373 result = self.doSearch("SELECT * FROM printers") 376 374 if result : 375 patterns = printerpattern.split(",") 377 376 for record in result : 378 377 pname = self.databaseToUserCharset(record["printername"]) 379 if self.tool.matchString(pname, p rinterpattern.split(",")) :378 if self.tool.matchString(pname, patterns) : 380 379 printer = StoragePrinter(self, pname) 381 380 printer.ident = record.get("id") … … 394 393 return printers 395 394 395 def getMatchingUsers(self, userpattern) : 396 """Returns the list of all users for which name matches a certain pattern.""" 397 users = [] 398 # We 'could' do a SELECT username FROM users WHERE username LIKE ... 399 # but we don't because other storages semantics may be different, so every 400 # storage should use fnmatch to match patterns and be storage agnostic 401 result = self.doSearch("SELECT * FROM users") 402 if result : 403 patterns = userpattern.split(",") 404 for record in result : 405 uname = self.databaseToUserCharset(record["username"]) 406 if self.tool.matchString(uname, patterns) : 407 user = StorageUser(self, uname) 408 user.ident = record.get("id") 409 user.LimitBy = record.get("limitby") or "quota" 410 user.AccountBalance = record.get("balance") 411 user.LifeTimePaid = record.get("lifetimepaid") 412 user.Email = record.get("email") 413 user.Description = self.databaseToUserCharset(record.get("description")) 414 user.OverCharge = record.get("overcharge", 1.0) 415 user.Exists = 1 416 users.append(user) 417 self.cacheEntry("USERS", user.Name, user) 418 return users 419 420 def getMatchingGroups(self, grouppattern) : 421 """Returns the list of all groups for which name matches a certain pattern.""" 422 groups = [] 423 # We 'could' do a SELECT groupname FROM groups WHERE groupname LIKE ... 424 # but we don't because other storages semantics may be different, so every 425 # storage should use fnmatch to match patterns and be storage agnostic 426 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) GROUP BY groups.id,groups.groupname,groups.limitby,groups.description") 427 if result : 428 patterns = grouppattern.split(",") 429 for record in result : 430 gname = self.databaseToUserCharset(record["groupname"]) 431 if self.tool.matchString(gname, patterns) : 432 group = StorageGroup(self, gname) 433 group.ident = record.get("id") 434 group.LimitBy = record.get("limitby") or "quota" 435 group.AccountBalance = record.get("balance") 436 group.LifeTimePaid = record.get("lifetimepaid") 437 group.Description = self.databaseToUserCharset(record.get("description")) 438 group.Exists = 1 439 groups.append(group) 440 self.cacheEntry("GROUPS", group.Name, group) 441 return groups 442 396 443 def getMatchingBillingCodes(self, billingcodepattern) : 397 444 """Returns the list of all billing codes for which the label matches a certain pattern.""" … … 399 446 result = self.doSearch("SELECT * FROM billingcodes") 400 447 if result : 448 patterns = billingcodepattern.split(",") 401 449 for record in result : 402 450 bcode = self.databaseToUserCharset(record["billingcode"]) 403 if self.tool.matchString(bcode, billingcodepattern.split(",")) :451 if self.tool.matchString(bcode, patterns) : 404 452 code = StorageBillingCode(self, bcode) 405 453 code.ident = record.get("id") -
pykota/trunk/pykota/tool.py
r2650 r2657 195 195 return self.charset 196 196 197 def display(self, message) : 198 """Display a message but only if stdout is a tty.""" 199 if sys.stdout.isatty() : 200 sys.stdout.write(message) 201 sys.stdout.flush() 202 197 203 def logdebug(self, message) : 198 204 """Logs something to debug output if debug is enabled."""