Exemple #1
0
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)
Exemple #2
0
class Contribution(db.TimeStampedBase):
    codebase = db.ForeignKey(kind=Codebase)
    contributor = db.ForeignKey(kind=Contributor)
    count = db.Integer(default=0)

    def handle(self):
        return self.contributor.get().handle

    def membership(self):
        from .core import Person, Membership
        person = Person.query(
            Person.contributors.contains(self.contributor.urlsafe())).get()
        pod = db.get(self.codebase).pod
        return person and pod and Membership.query(
            Membership.pod == pod, Membership.person == person.key).get()

    def total(self):
        return self.count * ratios.code.line

    def process(self, cbatch, diff, total=0):
        cbase = self.codebase.get()
        self.membership().deposit(
            diff * ratios.code.line, cbatch,
            "code commits: %s@%s" % (self.handle(), cbase.repo),
            "variety: %s\nowner: %s\nbatch: %s\ntotal: %s" %
            (cbase.variety, cbase.owner, diff, total or diff), True)

    def refresh(self, total, cbatch):
        diff = total - self.count
        if diff:
            self.process(cbatch, diff, total)
            self.count = total
            self.put()
        return diff
Exemple #3
0
class Resource(Place):
    editors = db.ForeignKey(kind="Person", repeated=True)
    name = db.String()
    description = db.Text()
    tags = db.ForeignKey(kind=Tag, repeated=True)
    icon = db.String()  # refers to ctmap graphic resource
    label = "name"

    def _pre_trans_zipcode(self, val):
        if isinstance(val, string_types) and len(val) < 10:
            val = getzip(val).key
        return val

    def total(self):
        return config.ctcomp.ratios.resource

    def oncreate(self):
        zcode = self.zipcode.get()
        addr = "%s, %s, %s" % (self.address, zcode.city, zcode.state)
        self.latitude, self.longitude = address2latlng(addr)

    def notify(self, podname, interested):
        bod = RESOURCE % (podname, self.name, self.description)
        for person in interested:
            send_mail(to=person.email, subject="new message board", body=bod)
Exemple #4
0
class Stewardship(db.TimeStampedBase):
    steward = db.ForeignKey()
    timeslots = db.ForeignKey(kind=Timeslot, repeated=True)

    def task(self):
        return Task.query(Task.commitments.contains(self.key.urlsafe())).get()

    def happening(self, now):
        slots = []
        if self.task().happening(now):
            for slot in db.get_multi(self.timeslots):
                if isDay(slot, now):
                    slots.append(slot)
        if len(slots) is 1:  # if 2, one is exception
            return slots[0]

    def beforeremove(self, session):
        task = self.task()
        if task:  # no task is task is deleting itself...
            task.unsteward(self)
            task.commitments = list(
                filter(lambda x: x != self.key, task.commitments))
            task.put(session)

    def afterremove(self, session):
        db.delete_multi(db.get_multi(self.timeslots, session), session)
Exemple #5
0
class Codebase(db.TimeStampedBase):
    pod = db.ForeignKey(kind="Pod")
    owner = db.String()  # bubbleboy14
    repo = db.String()  # ctcomp
    variety = db.String(choices=[
        "platform", "framework", "service", "research and development"
    ])
    dependencies = db.ForeignKey(kind="Codebase", repeated=True)
    label = "repo"

    def deposit(self, amount, deed):
        log('compensating "%s/%s" codebase: %s' %
            (self.owner, self.repo, amount))
        contz = self.contributions()
        total = float(sum([cont.count for cont in contz]))
        platcut = amount * ratios.code.get(self.variety, ratios.code.rnd)
        log('dividing %s cut (%s) among %s contributors' %
            (self.variety, platcut, len(contz)))
        details = "variety: %s\nowner: %s" % (self.variety, self.owner)
        for contrib in contz:
            memship = contrib.membership()
            memship and memship.deposit(
                platcut * contrib.count / total, deed, "code usage: %s@%s" %
                (contrib.handle(), self.repo), details, True)
        depcut = amount * ratios.code.dependency
        dnum = len(self.dependencies)
        if dnum:
            depshare = depcut / dnum
            log('dividing dependency cut (%s) among %s codebases' %
                (depcut, dnum))
            for dep in db.get_multi(self.dependencies):
                dep.deposit(depshare, deed)

    def contributions(self, asmap=False):
        clist = Contribution.query(Contribution.codebase == self.key).fetch()
        if not asmap:
            return clist
        contz = {}
        for cont in clist:
            contz[cont.contributor.get().handle] = cont
        return contz

    def refresh(self, cbatch):
        from .util import getContribution
        freshies = fetch("api.github.com",
                         "/repos/%s/%s/contributors" % (self.owner, self.repo),
                         asjson=True,
                         protocol="https")
        pcount = 0
        ccount = 0
        for item in freshies:
            log("checking for: %s" % (item["login"], ), 1)
            contrib = getContribution(self, item["login"])
            if contrib:
                pcount += 1
                ccount += contrib.refresh(item["contributions"], cbatch)
        return "%s/%s: %s contributors, %s contributions" % (
            self.owner, self.repo, pcount, ccount)
Exemple #6
0
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)
Exemple #7
0
class LibItem(db.TimeStampedBase):
    content = db.ForeignKey(kind="Content")
    editors = db.ForeignKey(kind="Person", repeated=True)
    name = db.String()
    description = db.Text()
    tags = db.ForeignKey(kind=Tag, repeated=True)
    label = "name"

    def notify(self, podname, interested):
        bod = LIBITEM % (podname, self.name, self.description)
        for person in interested:
            send_mail(to=person.email, subject="new message board", body=bod)
Exemple #8
0
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()
Exemple #9
0
class Membership(db.TimeStampedBase):
    pod = db.ForeignKey(kind=Pod)
    person = db.ForeignKey(kind=Person)
    proposals = db.ForeignKey(kind=Proposal, repeated=True)
    products = db.ForeignKey(kind=Product, repeated=True)

    def deposit(self,
                amount,
                deed,
                note,
                details=None,
                nocode=False,
                pay=False):
        self.pod.get().deposit(self.person.get(), amount, deed, note, details,
                               nocode, pay)
Exemple #10
0
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
Exemple #11
0
class Payment(Verifiable):
    payer = db.ForeignKey(kind="Person")
    amount = db.Float()

    def signers(self):
        return [self.payer]

    def fulfill(self):
        if self.passed or not self.verified():
            return False
        payer = self.payer.get()
        memship = self.membership.get()
        recip = memship.person.get()
        pod = memship.pod.get()
        payer.wallet.get().debit(self.amount, pod, self,
                                 "payment to %s" % (recip.email, ), self.notes)
        memship.deposit(self.amount,
                        self,
                        "payment from %s" % (payer.email, ),
                        self.notes,
                        pay=True)
        self.passed = True
        self.put()
        body = PAID % (self.amount, payer.email, recip.email, pod.name,
                       self.notes)
        for target in [payer, recip]:
            send_mail(to=target.email,
                      subject="payment confirmation",
                      body=body)
        return True
Exemple #12
0
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
Exemple #13
0
class View(db.TimeStampedBase):
    viewer = db.ForeignKey(kind=Person)
    content = db.ForeignKey(kind=Content)

    def process(self):
        content = self.content.get()
        mships = content.membership and [content.membership
                                         ] or content.memberships
        cut = ratios.view / len(mships)
        for memship in mships:
            membership = memship.get()
            membership.pod.get().deposit(membership.person.get(), cut, self,
                                         "viewed: %s" % (content.identifier, ),
                                         "view: %s" % (self.key.urlsafe(), ))

    def total(self):
        return ratios.view
Exemple #14
0
class Act(Verifiable):
    service = db.ForeignKey(kind="Service")
    workers = db.ForeignKey(kind="Person", repeated=True)
    beneficiaries = db.ForeignKey(kind="Person", repeated=True)

    def signers(self):
        return self.beneficiaries

    def fulfill(self):
        if self.passed or not self.verified():
            return False
        count = len(self.beneficiaries)
        pod = self.pod()
        service = self.service.get()
        for worker in db.get_multi(self.workers):
            pod.service(worker, service, count, self.notes)
        self.passed = True
        self.put()
        return True
Exemple #15
0
class Update(db.TimeStampedBase):
    sender = db.ForeignKey()
    subject = db.String()
    message = db.Text()
    recipients = db.ForeignKey(repeated=True)
    conversation = db.ForeignKey(kind=Conversation)
    label = "subject"

    def oncreate(self):
        convo = Conversation(topic=self.subject)
        convo.put()
        self.conversation = convo.key
        if self.recipients:
            recipients = db.get_multi(self.recipients)
        else:
            recipients = Member.query().all()
        bod = UPDATE % (self.sender.get().email, self.message)
        for recip in recipients:
            send_mail(to=recip.email, subject=self.subject, body=bod)
Exemple #16
0
class Commitment(Verifiable):
    service = db.ForeignKey(kind="Service")
    estimate = db.Float(default=1.0)  # per week (hours?)

    def deposit(self, numdays=1):
        service = self.service.get()
        details = "compensating commitment: %s service (%s); estimated %s hours per week; paying for %s days" % (
            service.name, service.compensation, self.estimate, numdays)
        log(details)
        self.membership.get().deposit(
            service.compensation * self.estimate * numdays / 7.0, self,
            "commitment: %s" % (service.name, ), details)
Exemple #17
0
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
Exemple #18
0
class Task(db.TimeStampedBase):
    editors = db.ForeignKey(repeated=True)
    timeslots = db.ForeignKey(kind=Timeslot, repeated=True)
    commitments = db.ForeignKey(kind=Stewardship, repeated=True)
    name = db.String()
    description = db.Text()
    mode = db.String()  # arbitrary
    requirements = db.String(repeated=True)
    steps = db.String(repeated=True)

    def happening(self, now):
        slots = []
        for slot in db.get_multi(self.timeslots):
            if isDay(slot, now):
                slots.append(slot)
        if len(slots) is 1:  # if 2, one is exception
            return slots[0]

    def unsteward(self, stewardship, verb="rescheduled"):  # just a notifier
        send_mail(to=stewardship.steward.get().email,
                  subject="commitment update",
                  body=RESCHED % (self.name, verb))

    def downschedule(self):
        stewz = db.get_multi(self.commitments)
        for stew in stewz:
            self.unsteward(stew, "rescheduled")
        self.commitments = []
        self.put()
        db.delete_multi(stewz)

    def beforeremove(self, session):
        for stew in db.get_multi(self.commitments, session):
            self.unsteward(stew, "removed")

    def afterremove(self, session):
        db.delete_multi(
            db.get_multi(self.timeslots + self.commitments, session), session)
Exemple #19
0
class Delivery(Verifiable):
    driver = db.ForeignKey(kind="Person")
    miles = db.Integer()

    def signers(self):
        return [self.membership.get().person, self.driver]

    def fulfill(self):
        if self.passed or not self.verified():
            return False
        self.pod().deposit(self.driver.get(),
                           ratios.delivery + ratios.mileage * self.miles, self,
                           "delivery: %s miles" % (self.miles, ), self.notes)
        self.passed = True
        self.put()
        return True
Exemple #20
0
class SecBase(db.TimeStampedBase):
    name = db.String()
    description = db.Text()
    sections = db.ForeignKey(kind="section", repeated=True)

    def secs(self, sections=None, depth=0, novars=False, page_breaks=False):
        return "\r\n\r\n".join(sections and [
            db.get(s['key']).content(s['sections'], depth, novars, page_breaks)
            for s in sections
        ] or [
            s.content(depth=depth, novars=novars, page_breaks=page_breaks)
            for s in db.get_multi(self.sections)
        ])

    def fixed_desc(self, depth=0, novars=False):
        d = self.description
        return h2l(novars and d.replace("{{", "(").replace("}}", ")") or d,
                   depth)

    def desc(self, depth=0, novars=False):
        return self.fixed_desc(depth, novars)

    def header(self):
        return self.name

    def body(self, depth, novars=False, page_breaks=False):
        tline = "%s %s" % ("#" * depth, self.header())
        if page_breaks and depth == 1:
            tline = "\\newpage%s" % (tline, )
        return "%s\r\n\r\n%s" % (tline, self.desc(depth, novars))

    def content(self, sections=None, depth=0, novars=False, page_breaks=False):
        body = self.body(depth, novars, page_breaks)
        secs = self.sections and self.secs(sections, depth + 1, novars,
                                           page_breaks) or ""
        cont = "%s\r\n\r\n%s" % (body, secs)
        log(cont)
        return cont

    def unrolled(self):
        d = self.data()
        d['sections'] = [s.unrolled() for s in db.get_multi(self.sections)]
        return d
Exemple #21
0
class Invitation(db.TimeStampedBase):
    membership = db.ForeignKey(kind=Membership)
    email = db.String()
    notes = db.Text()

    def invite(self):
        memship = self.membership.get()
        send_mail(to=self.email,
                  subject="invitation",
                  body=INVITATION %
                  (memship.person.get().email, memship.pod.get().name))

    def send(self, person):
        req = Request()
        req.membership = self.membership
        req.person = person.key
        req.change = "include"
        req.notes = self.notes
        req.put()
        req.remind()
Exemple #22
0
class Appointment(Verifiable):
    timeslot = db.ForeignKey(kind=Timeslot)

    def signers(self):
        return self.task().editors

    def task(self):
        return self.stewardship().task()

    def stewardship(self):
        return self.timeslot.get().slotter()

    def fulfill(self):
        if not self.verified():
            return False
        self.membership.get().deposit(self.timeslot.get().duration, self,
                                      "appointment: %s" % (self.task().name, ),
                                      self.notes)
        self.passed = True
        self.put()
        return True
Exemple #23
0
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)
Exemple #24
0
class Payment(db.TimeStampedBase):
    member = db.ForeignKey(kind=Member)
    successful = db.Boolean(default=False)
    amount = db.String()
    duration = db.Integer()  # days
    message = db.Text()
Exemple #25
0
class Template(SecBase):
    owner = db.ForeignKey(kind=Member)
    injections = db.ForeignKey(kind=Injection, repeated=True)

    def body(self, depth, novars=False, page_breaks=False):
        return self.desc(depth, novars)
Exemple #26
0
class Pod(db.TimeStampedBase):
    name = db.String()
    variety = db.String()
    blurb = db.Text()
    pool = db.ForeignKey(kind=Wallet)
    agent = db.ForeignKey(kind="Pod")
    needs = db.ForeignKey(kind=Need, repeated=True)
    tasks = db.ForeignKey(kind=Task, repeated=True)
    boards = db.ForeignKey(kind=Board, repeated=True)
    updates = db.ForeignKey(kind=Update, repeated=True)
    drivers = db.ForeignKey(kind=Person, repeated=True)
    includers = db.ForeignKey(kind=Person, repeated=True)
    resources = db.ForeignKey(kind=Resource, repeated=True)
    offerings = db.ForeignKey(kind=Offering, repeated=True)
    dependencies = db.ForeignKey(kind=Codebase, repeated=True)  # software
    library = db.ForeignKey(kinds=[Organization, Book, Web, Media],
                            repeated=True)  # support

    def _trans_boards(self, val):
        v = val[-1].get()
        v.notify(self.name, self.interested(v.tags))
        return val

    def _trans_library(self, val):
        v = val[-1].get()
        v.notify(self.name, self.interested(v.tags))
        return val

    def _trans_resources(self, val):
        v = val[-1].get()
        v.notify(self.name, self.interested(v.tags))
        return val

    def _trans_needs(self, val):
        self.notify(val[-1].get(), NEED)
        return val

    def _trans_offerings(self, val):
        self.notify(val[-1].get(), OFFERING)
        return val

    def notify(self, item, etemp):
        bod = etemp % (self.name, item.description)
        for person in self.interested(item.tags):
            send_mail(to=person.email,
                      subject="new %s" % (item.polytype, ),
                      body=bod)

    def interested(self, tags):
        tagz = set(map(lambda t: t.urlsafe(), tags))
        return filter(
            lambda p: tagz.intersection(
                set(map(lambda t: t.urlsafe(), p.interests))),
            db.get_multi(self.members()))

    def oncreate(self):
        email_admins("New Pod",
                     "name: %s\nvariety: %s" % (self.name, self.variety))
        if not self.pool:
            w = Wallet()
            w.put()
            self.pool = w.key

    def codebases(self):
        return Codebase.query(Codebase.pod == self.key).fetch()

    def _collection(self, mod):
        return sum([
            mod.query(mod.membership == m.key).fetch()
            for m in self.members(True)
        ], [])

    def expenses(self):
        return self._collection(Expense)

    def acts(self):
        return self._collection(Act)

    def requests(self):
        return self._collection(Request)

    def commitments(self):
        return self._collection(Commitment)

    def proposals(self):
        return sum([m.proposals for m in self.members(True)], [])

    def members(self, noperson=False):
        mems = Membership.query(Membership.pod == self.key).fetch()
        return noperson and mems or [mem.person for mem in mems]

    def deposit(self,
                member,
                amount,
                deed,
                note,
                details=None,
                nocode=False,
                pay=False):
        memwall = member.wallet.get()
        if pay:
            memcut = amount * ratios.pay
            amount -= memcut
            memwall.deposit(memcut, self, deed, note, details)
        else:
            memwall.deposit(amount, self, deed, note, details)
        self.pool.get().deposit(amount, self, deed, note, details)
        self.agent and self.agent.get().pool.get().deposit(
            amount * ratios.agent, self, deed, note, details)
        if not nocode:
            for codebase in self.codebases():
                codebase.deposit(amount, deed)
            depcut = amount * ratios.code.dependency
            for dependency in db.get_multi(self.dependencies):
                dependency.deposit(depcut, deed)

    def service(self, member, service, recipient_count, details):
        self.deposit(member, service.compensation * recipient_count, service,
                     "service: %s (%s)" % (service.name, service.variety),
                     details)

    def support_service(self):
        sname = (self.variety == "support") and self.name or "support"
        service = Service.query(Service.name == sname,
                                Service.variety == self.variety).get()
        if not service:
            service = Service(name=sname, variety=self.variety)
            service.put()
        return service.key
Exemple #27
0
class PhotoSet(BasePost):
    photos = db.ForeignKey(repeated=True, kind=Photo)
Exemple #28
0
class Content(db.TimeStampedBase):
    membership = db.ForeignKey(kind=Membership)
    memberships = db.ForeignKey(kind=Membership, repeated=True)
    identifier = db.String()  # some hash, defaulting to url
Exemple #29
0
class Comment(db.TimeStampedBase):
    user = db.ForeignKey()  # CTUser, Author, or whatever else
    post = db.ForeignKey(kinds=["post", "videopost", "photoset"])
    body = db.Text()
Exemple #30
0
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()
        ], [])