class X(BlackBox): def get_a(self): return 1 get_b = lambda self: 2 c = attr(int) d = attr(int) e = attr(int, default=5) def set_c(self, value): self.d = 4 def set_e(self, value): pass
class User(Strongbox): ID = attr(long, default=None) uid = attr(str) username = attr(str) email = attr(str) password = attr(str) def __init__(self, **init): super(User, self).__init__(**init) if not self.uid: self.uid = handy.uid()
class Item(Strongbox): """ A line item inside a Transaction. """ account = attr(str) amount = attr(Decimal) implied = attr(bool, default=False) state = attr(str, okay=[CLEARED, PENDING, DEFAULT]) charPos = attr(int, default=0) def clone(self): return Item(account=self.account, amount=Decimal(self.amount))
class Cyclic(Strongbox): """ Mixin class for cyclic behavior. By default, this uses a monthly cycle. Classes that use this should assign nextDue in their constructors. """ __ver__ = "$Id: Cyclic.py,v 1.15 2007/03/30 00:29:18 sabren Exp $" nextDue = attr(Date) cycLen = attr(str, okay=["month", "year"], default="month") def get_cycle(self): if self.cycLen == "year": return YearlyCycle(self) else: return MonthlyCycle(self) def calcNextDue(self): """ Returns one month after current due date. """ return self.get_cycle().nextDue() def isDue(self): # note that we use duckbill.TODAY rather than TODAY # so we can test more easily. # @TODO: this really ought to call a function, # even for testing... (???) return (self.nextDue is not None) \ and (self.nextDue <= duckbill.TODAY) def catchup(self): wasDue = 0 while self.isDue(): wasDue = 1 self.onDue() self.nextDue = self.calcNextDue() self.onCatchup(wasDue) ## events ################################################# def onDue(self): pass def onCatchup(self, wasDue): pass
class Node(Strongbox): ID = attr(long) name = attr(str) path = attr(str) data = attr(str) parent = link(lambda: Node) children = linkset((lambda: sixthday.Node), "parent") def __init__(self, **kwargs): super(Node, self).__init__() self.private.named = False self.update(**kwargs) ## def set_path(self, value): ## # only allow setting once ## if self.private.path: ## raise AttributeError, "Node.path is read only" def get_crumbs(self): res = [] node = self while node.parent: node = node.parent res.append(node) res.reverse() # crumbs go top-down, but we went bottom-up :) return res ## def set_parent(self, value): ## assert value is not self, \ ## "A node can't be its own parent!" def set_name(self, value): self.private.name = str(value) # this .private.named thing prevents a max # bug of some kind. It probably needs a # closer look. if self.private.named: self._updatePath(self.crumbs) self.private.named = True def _updatePath(self, crumbs): path = crumbs + [self] self.path = "/".join([n.name for n in path]) for kid in self.children: kid._updatePath(path)
class Card(Strongbox): expMonth = attr(int) expYear = attr(int) number = attr(str) def getEditableAttrs(self): return super(Card, self).getEditableAttrs() + ["masked", "issuer"] def _new(self): super(Card, self)._new() nowYear, nowMonth = time.localtime(time.time())[0:2] self.expYear = nowYear self.expMonth = nowMonth self.customerID = 0 def get_issuer(self): return issuer(self.number) def get_masked(self): return ("x" * (len(self.number) - 4)) + self.number[-4:] def checkdigits(self, digits): return validate(digits) def isExpired(self): import time # @TODO: does a card expire on the first or last of expiration month? nowYear, nowMonth = time.localtime(time.time())[0:2] return (nowYear, nowMonth) > (self.expYear, self.expMonth) def set_number(self, value): # Strip dashes and spaces.. # I'm leaving letters and other characters in # so that they generate an error. num = "" for ch in str(value): if ch not in "- ": num = num + ch # validate the card: if (issuer(num) != "unknown") and checkLength(issuer(num), len(num)) \ and self.checkdigits(num): self.__values__['number'] = num else: raise ValueError, "Invalid credit card number."
def test_simple(self): Person = MetaBox("Person", (), {"name": attr(str, okay=["fred", "wanda"], default="fred")}) assert type(Person) == MetaBox assert isinstance(Person.name, attr) assert Person.name.okay == ["fred", "wanda"] assert Person.name.__name__ == "name" assert Person.name.__owner__ == Person
class Right(zdc.RecordObject): """ Another test class for use with Junction. """ __super = zdc.RecordObject _tablename = "test_right" ID = attr(long) def _new(self): self._super._new(self) self.name = ""
def test_simple(self): Person = MetaBox( "Person", (), { "name": attr(str, okay=['fred', 'wanda'], default="fred"), }) assert type(Person) == MetaBox assert isinstance(Person.name, attr) assert Person.name.okay == ['fred', 'wanda'] assert Person.name.__name__ == "name" assert Person.name.__owner__ == Person
class Foo(BlackBox): m_str = attr(str) m_int = attr(int) m_long = attr(float) m_float = attr(float) n_int = attr(int, default=None) n_str = attr(str, default=None)
class MockCyclic(duckbill.Cyclic): nextDue = attr(Date) # @TODO: strongbox should support inheritance! def __init__(self): super(MockCyclic, self).__init__() self.private.calls = {"onDue":0, "onCatchup":0} self.nextDue = YESTERDAY def calcNextDue(self): return self.nextDue + 1 def onDue(self): self.private.calls["onDue"] += 1 def onCatchup(self, wasDue): self.private.calls["onCatchup"] += 1 self.private.wasDue = wasDue
class Left(zdc.RecordObject): """ A test class for a Junction. """ __super = zdc.RecordObject _tablename = "test_left" ID = attr(long) def _new(self): self._super._new(self) self.name = "" #@TODO: is this really the best way to do this? def get_rights(self): if not self._data.has_key("rights"): self._data["rights"] = zdc.Junction(self, Left, "test_left_right", "leftID", "rightID") self._data["rights"].fetch() return self._data["rights"]
class Paint(BlackBox): color = attr(str, ["red", "green", "blue"])
class Model(Strongbox): name = attr(str) age = attr(int, default=None) prefill = attr(str, default="example") limited = attr(str, okay=["a", "b", "c"], default="a")
class Subject(WhiteBox): name = attr(str)
class Dirt(WhiteBox): x = attr(str)
class SlotMachine(BlackBox): a = attr(int, default=1) b = attr(int, default=2) c = attr(int, default=3)
class Y(BlackBox): a = attr(int) b = attr(str) c = link(X) d = link(X)
class Foo(BlackBox): i = attr(int) s = attr(str) d = attr(Date)
class Dad(BlackBox): nose = attr(str, default="big")
class Foo(BlackBox): bar = attr(int, default=5)
class Node(Strongbox): ID = attr(long) data = attr(str) parent = link(forward) kids = linkset(forward, "parent")
class Foo(BlackBox): bar = attr(UpCase, default="xyz") abc = attr(str, default="xyz")
class Child(Strongbox): mama = link(forward) name = attr(str)
class Foo(BlackBox): bar = attr(int, lambda x: 5 < x < 10)
class Account(Cyclic): """ An account contains Subscriptions and Events """ __ver__ = "$Id: Account.py,v 1.37 2007/05/21 03:18:03 sabren Exp $" ID = attr(long) #@TODO: unhardcode the SEI-specific brand stuff (make Brand class instead) brand = attr(str, default="cornerhost", okay=["cornerhost", "versionhost", "dcd hosting"]) fname = attr(unicode, default="") lname = attr(unicode, default="") email = attr(str, default="") email2 = attr(str, default="") phone = attr(str, default="") company = attr(unicode, default="") address1 = attr(unicode, default="") address2 = attr(unicode, default="") city = attr(unicode, default="") state = attr(unicode, default="") postal = attr(str, default="") countryCD = attr(str, default="") account = attr(str, default="") opened = attr(Date, default="today", allowNone=0) closed = attr(Date) cycLen = attr(str, okay=['month', 'year'], default='month') nextDue = attr(Date, default="today") autobill = attr(int, default=0, okay=[1, 0]) cardinfo = attr(EncryptedCard, default="") lastfour = attr(str) statementStrategy = attr(str, default="always", okay=["always", "ifBalance"]) status = attr( str, default='active', okay=['active', 'warned', 'locked', 'closed', 'comped', "lifer"]) warned = attr(Date) subscriptions = linkset(Subscription, "account") events = linkset(Event, "account") # @TODO: making gracePeriod a link breaks FrontEndAppTest.test_closeAccount!! # For some reason, Clerk just doesn't handle 1-1 relations well. :( # perhaps because the column in the schema dict is "ID" and that causes # clerk to remove it from the data it sends to storage.store() ? # (it was triggering an insert instead of an update, but ONLY # after acc.close()) gracePeriods = linkset(Grace, "account") def get_graced(self): return [g for g in self.gracePeriods if not g.hasExpired()] def grace(self, why, untilWhen): self.gracePeriods << Grace(reason=why, expires=untilWhen) def onCatchup(self, wasDue): """ Send the statement via email and record the fact that we did so. """ pass #if wasDue: # self.sendCurrentStatement() # #@TODO: "and record the fact that we did so" def sendCurrentStatement(self): sendmail(zebra.fetch("statement", self.currentStatement())) def currentStatement(self): return Statement(self, self.whenLastStatementPosted(), duckbill.NOW) def whenLastStatementPosted(self): """ Return date of last statement, or date opened if no statement ever sent. """ res = self.opened for e in self.events: if (e.event == "statement") and (e.posted > res): res = e.posted return res def balance(self, time=None): """ Return account balance at given time, or now if no time given. """ cutoff = time or duckbill.NOW bal = Decimal('0.0') for e in self.events: if e.posted < cutoff: bal += ((e.amount or 0) * e.sign) return bal def eventsBetween(self, start, end): """ Uses the python style of inclusive start, exclusive end. """ res = [e for e in self.events if start <= e.posted < end] res.sort() return tuple(res) def postCharges(self): """ post charges for the account's subscriptions """ for sub in self.subscriptions: sub.catchup() def aging(self): credit = 0 charges = [] def ischarge(x): return x > 0 def iscredit(x): return x < 0 for e in self.events: # whenever a new event is considered, we add # in any credit balance that might be there. amount = e.value + credit credit = 0 # now apply this new merged value to the account: if ischarge(amount): charges.append( Event(event="charge", amount=amount, posted=e.posted)) else: cash = abs(amount) while cash > 0: if charges: # apply amount to latest charge owed = abs(charges[-1].amount) if cash >= owed: charges.pop() cash -= owed else: charges[-1].amount -= cash cash = 0 else: credit -= cash # because credit is negative cash = 0 return charges def amountPastDue(self, dueDate="today"): """ Given a dueDate, return the amount past due. This may be less than the total balance. """ d = dueDate if not isinstance(d, Date): d = Date(dueDate) res = 0 for e in self.aging(): if e.posted < dueDate: res += e.value return res def unearnedIncome(self, cutoff): assert isinstance(cutoff, DateTime) sum = 0 for e in self.events: if e.posted <= cutoff: sum += e.valueOn(cutoff) return -min(sum, 0) def close(self, why): self.status = "closed" self.closed = duckbill.TODAY for s in self.subscriptions: s.close() self.events << Event(event="close", note=why)
class UsCitizen(BlackBox): ssn = attr(str, r"\d{3}-?\d{2}-?\d{4}")
class Fish(zdc.RecordObject): _tablename = "test_fish" ID = attr(long) fish = attr(str)
class Foo(BlackBox): bar = attr(int)
class Record(Strongbox): ID = attr(long) val = attr(str) next = link(forward)
class Foo(BlackBox): bar = attr(int,allowNone=0)