class Document(db.TimeStampedBase): owner = db.ForeignKey(kind=Member) logo = db.Binary() template = db.ForeignKey(kind=Template) name = db.String() injections = db.JSON() assembly = db.JSON() declarations = db.JSON() pdf = db.String() revision = db.Integer(default=0) signup_sheet = db.Boolean(default=True) table_of_contents = db.Boolean(default=True) declaration_page = db.Boolean(default=True) pretty_filenames = db.Boolean(default=True) section_page_breaks = db.Boolean(default=False) def summary(self): d = { "key": self.id(), "name": self.name, "revision": self.revision, "pdf": self.pdf, "created": str(self.created)[:19], "modified": str(self.modified)[:19], "declarations": self.declarations or {} } if self.template: t = self.template.get() d["template"] = {"key": t.id(), "name": t.name} return d def content(self, sections=None): return self.template.get().content( sections, page_breaks=self.section_page_breaks)
class Contactable(db.TimeStampedBase): tags = db.ForeignKey(kind=Tag, repeated=True) member = db.ForeignKey() name = db.String() email = db.String() phone = db.String() address = db.String() description = db.Text() # only required field closed = db.Boolean(default=False) ongoing = db.Boolean(default=False)
class Expense(Verifiable): executor = db.ForeignKey(kind="Person") # reimbursement only variety = db.String(choices=["dividend", "reimbursement"]) amount = db.Float(default=0.1) # for dividend, split amount * total recurring = db.Boolean(default=False) def dividend(self): pod = self.pod() pool = pod.pool.get() people = db.get_multi(pod.members()) div = self.amount * pool.outstanding cut = div / len(people) for person in people: person.wallet.get().deposit(cut, pod, self, "dividend") pool.debit(div, pod, self, "dividend") # reimbursement requires $$ conversion... def reimbursement(self): pass def fulfill(self): if (self.passed and not self.recurring) or not self.verified(): return False getattr(self, self.variety)() return True
class Verifiable(db.TimeStampedBase): membership = db.ForeignKey(kind="Membership") passed = db.Boolean(default=False) notes = db.Text() def pod(self, noget=False): pod = self.membership.get().pod return noget and pod or pod.get() def signers(self): return self.pod().members() def fulfill(self): if not self.verified(): return False self.passed = True self.put() return True def notify(self, subject, body, signers=None): for signer in (signers or self.signers()): if self.unverified(signer): send_mail(to=signer.get().email, subject=subject, body=body(signer)) def unverify(self): log("unverifying %s" % (self.key.urlsafe(), )) sigs = Verification.query(Verification.act == self.key).fetch() log("unsigning %s verifications" % (len(sigs), ), 1) db.delete_multi(sigs) log("unpassing", 1) self.passed = False self.put() def verify(self, person): if person in self.signers(): if Verification.query(Verification.act == self.key, Verification.person == person).get(): return log("already verified (%s %s)!" % (self.key, person), important=True) log("verification (%s %s) success" % (self.key.urlsafe(), person.urlsafe())) Verification(act=self.key, person=person).put() return self.fulfill() log("verification attempt (%s %s) failed -- unauthorized" % (self.key, person)) def veriquery(self): return Verification.query(Verification.act == self.key) def unverified(self, person): return not self.veriquery().filter(Verification.person == person).get() def verified(self): for person in self.signers(): if self.unverified(person): return False return True
class Feedback(db.TimeStampedBase): person = db.ForeignKey(kind=Person) conversation = db.ForeignKey(kind=Conversation) interaction = db.ForeignKey(kinds=[Appointment, Delivery, Request]) answers = db.ForeignKey(kind=Answer, repeated=True) topic = db.String() notes = db.Text() followup = db.Boolean(default=False) def membership(self): from .util import membership return membership(self.person.get(), self.pod()) def pod(self): return self.interaction.get().pod() def full(self): answers = "\n\n".join([a.full() for a in db.get_multi(self.answers)]) return "\n\n".join([ self.topic, answers, self.notes, "request follow up: %s" % (self.followup, ) ]) def notify(self): bod = FEEDBACK % (self.person.get().firstName, self.pod().name, self.full(), self.key.urlsafe()) self.interaction.get().notify("feedback", lambda signer: bod, self.participants()) def participants(self): pars = self.interaction.get().signers() if self.person not in pars: return pars + [self.person] return pars def oncreate(self): convo = Conversation(topic=self.topic) convo.participants = self.participants() convo.put() self.conversation = convo.key self.put() # for notify() key self.notify() if self.followup: req = Request() req.membership = self.membership().key req.change = "conversation" req.notes = self.notes req.put() req.remind()
class Section(SecBase): image = db.Binary() headerless = db.Boolean(default=False) def labeler(self): return "%s [%s]" % (self.name, self.index) def header(self): return self.headerless and " " or self.name def desc(self, depth=0, novars=False): d = self.fixed_desc(depth, novars) if not self.image: return d return "%s\r\n\r\n![](%s)" % (d, symage(self.image.path))
class Board(db.TimeStampedBase): name = db.String() description = db.Text() anonymous = db.Boolean(default=False) tags = db.ForeignKey(kind=Tag, repeated=True) conversation = db.ForeignKey(kind=Conversation) label = "name" def pod(self): from .core import Pod return Pod.query(Pod.boards.contains(self.key.urlsafe())).get() def notify(self, podname, interested): bod = BOARD % (podname, self.name, self.description) for person in interested: send_mail(to=person.email, subject="new message board", body=bod) def oncreate(self): convo = Conversation(topic=self.name) convo.anonymous = self.anonymous convo.put() self.conversation = convo.key
class Payment(db.TimeStampedBase): member = db.ForeignKey(kind=Member) successful = db.Boolean(default=False) amount = db.String() duration = db.Integer() # days message = db.Text()
class Person(Member): ip = db.String() # optional wallet = db.ForeignKey(kind=Wallet) # optional interests = db.ForeignKey(kind=Tag, repeated=True) contributors = db.ForeignKey(kind=Contributor, repeated=True) chat = db.Boolean(default=True) remind = db.Boolean(default=True) def onjoin(self): from .util import global_pod email_admins("New Person", self.email) self.enroll(global_pod()) wallet = Wallet() wallet.put() self.wallet = wallet.key self.put() self.process_invites() def process_invites(self): log("processing invitations for %s" % (self.email, )) podz = set() for invitation in Invitation.query( Invitation.email == self.email).fetch(): imem = invitation.membership.get() ipod = imem.pod.get().name memem = imem.person.get().email log("pod: %s. inviter: %s" % (ipod, memem), 1) if ipod in podz: log("skipping invitation -- already invited to pod", 2) else: log("sending invitation", 2) podz.add(ipod) invitation.send(self) def help_match(self, item): # overrides Member.help_match() in ctcoop.model from .util import membership, reg_act which = item.polytype isneed = which == "need" pod = Pod.query( getattr(Pod, which + "s").contains(item.key.urlsafe())).get() reg_act( membership(self, pod).key, pod.support_service(), [isneed and self.key or item.member], [isneed and item.member or self.key], item.description) def enroll(self, pod): from .util import membership memship = membership(self, pod) if not memship: memship = Membership(pod=pod.key, person=self.key) memship.put() return memship.key def tasks(self): return db.get_multi(sum([p.tasks for p in self.pods()], [])) def pods(self): return db.get_multi([m.pod for m in self.memberships()]) def memberships(self): return Membership.query(Membership.person == self.key).fetch() def acts(self): yesterday = datetime.now() - timedelta(1) return sum([ Act.query(Act.membership == m.key, Act.created > yesterday).fetch() for m in self.memberships() ], []) def commitments(self): return sum([ Commitment.query(Commitment.membership == m.key).fetch() for m in self.memberships() ], [])
class BasePost(db.TimeStampedBase): user = db.ForeignKey() # CTUser, Author, or whatever else live = db.Boolean(default=False) title = db.String() blurb = db.String() tags = db.String(repeated=True)
class Building(Place): year = db.Integer() building_id = db.String() building_type = db.String() owner = db.ForeignKey(kind="owner") rent_control = db.Boolean()