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)
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 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)
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 Timeslot(db.TimeStampedBase): schedule = db.String(choices=[ "once", "weekly", "daily", "exception", "offday", "monthly (date)", "monthly (day)" ]) when = db.DateTime() duration = db.Float() # hours def stewardship(self): return Stewardship.query( Stewardship.timeslots.contains(self.key.urlsafe())).get() def task(self): return Task.query(Task.timeslots.contains(self.key.urlsafe())).get() def slotter(self): return self.task() or self.stewardship() def beforeedit(self, edits): sched = edits.get("sched") if sched == "daily" or sched == "weekly" and self.schedule == "once": return task = self.task() task and task.downschedule() def beforeremove(self, session): slotter = self.slotter() if slotter: # no slotter if slotter is deleting itself.... slotter.timeslots = list( filter(lambda x: x != self.key, slotter.timeslots)) slotter.put(session)
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 Answer(db.TimeStampedBase): prompt = db.String() response = db.Text() rating = db.Integer() # 1-5 def full(self): return "\n".join([self.prompt, self.response, str(self.rating)])
class ZipCode(db.ModelBase): code = db.String() city = db.String() state = db.String() county = db.String() latitude = db.Float() longitude = db.Float() label = "code" def __str__(self): return self.code def latlng(self): self.latitude, self.longitude = address2latlng(self.code) self.put() def fullString(self): return "%s, %s, %s" % (self.city, self.state, self.code)
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)
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)
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 Race(db.ModelBase): draw_num = db.Integer() draw_date = db.DateTime() win_num = db.Integer() win_name = db.String() place_num = db.Integer() place_name = db.String() show_num = db.Integer() show_name = db.String() race_time = db.String() def data(self): return { "key": self.key.urlsafe(), "draw_date": str(self.draw_date), "draw_num": self.draw_num, "win_num": self.win_num, "win_name": self.win_name, "place_num": self.place_num, "place_name": self.place_name, "show_num": self.show_num, "show_name": self.show_name, "race_time": self.race_time }
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)
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
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()
class Adjustment(Proposal): variety = db.String() # already has name! compensation = db.Float(default=1.0) def oncreate(self): convo = Conversation(topic=self.name) convo.put() self.conversation = convo.key service = self.service() self.notify( "compensation adjustment proposed", lambda signer: ADJUSTMENT % (self.name, self.variety, service.compensation, self.compensation, self.description)) def onpass(self): serv = self.service() self.notify( "compensation adjustment approved", lambda signer: ADJUSTED % (self.name, self.variety, serv.compensation, self.compensation, self.description)) serv.compensation = self.compensation serv.put() def notify(self, subject, body): for signer in self.voters(): send_mail(to=signer.get().email, subject=subject, body=body(signer)) def service(self): return Service.query(Service.name == self.name, Service.variety == self.variety).get() def voters(self): peeps = set() for pod in Pod.query(Pod.variety == self.variety).all(): peeps.update([k.urlsafe() for k in pod.members()]) return [db.KeyWrapper(p) for p in list(peeps)] def votership(self): return len(self.voters())
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 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 Payment(db.TimeStampedBase): member = db.ForeignKey(kind=Member) successful = db.Boolean(default=False) amount = db.String() duration = db.Integer() # days message = db.Text()
class Chemical(db.TimeStampedBase): classification = db.String() code = db.String() name = db.String() cas = db.String() rtecs = db.String() dot = db.String() idlh = db.String() synonyms_and_trade_names = db.String() formula = db.String() conversion = db.String() physical_description = db.String() molecular_weight = db.String() boiling_point = db.String() melting_point = db.String() freezing_point = db.String() solubility = db.String() vapor_pressure = db.String() ionization_potential = db.String() specific_gravity = db.String() flash_point = db.String() upper_explosive_limit = db.String() lower_explosive_limit = db.String() incompatibilities_and_reactivities = db.String() exposure_routes = db.String() symptoms = db.String() target_organs = db.String() cancer_site = db.String() respirator_recommendations = db.Text() exposure_limits = db.Text() measurement_methods = db.Text() first_aid = db.Text() personal_protection_sanitation = db.Text() def basic(self): return {"key": self.id(), "name": self.name}
class Table(db.TimeStampedBase): name = db.String() csv = db.Binary()
class Survey(db.TimeStampedBase): user = db.ForeignKey(kind=CTUser) demographics = db.ForeignKey(kind=Demographic, repeated=True) title = db.String() blurb = db.Text() instructions = db.Text()
class Injection(db.TimeStampedBase): name = db.String() variety = db.String(choices=["text", "text block"]) def labeler(self): return "%s (%s)" % (self.name, self.variety)
class Tag(db.TimeStampedBase): name = db.String()
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
class Service(db.TimeStampedBase): name = db.String() variety = db.String() compensation = db.Float(default=1.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
class Member(CTUser): roles = db.String(repeated=True) def help_match(self, need_or_offer): pass
class Photo(db.TimeStampedBase): img = db.Binary() caption = db.String() label = "caption"
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)