import random, re from itertools import groupby from twisted.python.components import registerAdapter from nevow.accessors import ObjectContainer from nevow.inevow import IContainer from axiom.item import Item, transacted from axiom.attributes import text, integer, timestamp, reference, AND from axiom.upgrade import registerUpgrader from epsilon.extime import Time from circus.icircus import IQuoteDatabase _nicknamePatterns = [ re.compile(r'<[ +%@&~]?(.*?) ?> '), # Addressing: < foo> hi <@bar> hi there re.compile(r'^\s*\* (.*?) '), # Actions: * foo does a thing ] def extractParticipants(content): """ Extract a list of unique nicknames that participate in a quote. @param content: Text representation of a quote's content @type content: C{unicode} @returns: A sequence of featured nicknames. """ nicknames = set() for line in content.splitlines(): for pattern in _nicknamePatterns: match = pattern.search(line) if match is not None and match.group(1): nicknames.add(match.group(1)) break return tuple(nicknames) def createParticipants(quote): """ Create L{Clown} items for each participant in C{quote}. @type quote: L{Quote} """ store = quote.store store.query(Clown, Clown.quote == quote).deleteFromStore(); for nickname in extractParticipants(quote.content): Clown(store=store, quote=quote, nickname=nickname) class Quote(Item): """ I represent a posted quote and provide methods to vote for the quote and against it, as well as keeping track of votes and ratings. """ typeName = 'flyingcircus_quote' schemaVersion = 1 qid = integer(doc=""" Quote ID. """) content = text(doc=""" The C{Quote}'s content. """) added = timestamp(doc=""" The C{Quote}'s creation timestamp. """) votesFor = integer(doc=""" The number of votes for this C{Quote}. """, default=0) votesAgainst = integer(doc=""" The number of votes against this C{Quote}. """, default=0) rating = integer(doc=""" The C{Quote}'s rating. This is equivalent to C{Quote.votesFor - Quote.votesAgainst}. """, default=0) votes = integer(doc=""" The total number of votes for this C{Quote}. """, default=0) @transacted def plus(self): """ Perform a "plus" vote. """ self.votesFor = self.votesFor + 1 self.rating = self.rating + 1 self.votes = self.votes + 1 @transacted def minus(self): """ Perform a "minus" vote. """ self.votesAgainst = self.votesAgainst + 1 self.rating = self.rating - 1 self.votes = self.votes + 1 @property def participants(self): return self.store.query(Clown, Clown.quote == self).getColumn('nickname') registerAdapter(ObjectContainer, Quote, IContainer) class Clown(Item): """ I represent a participant in a L{Quote}. """ typeName = 'flyingcircus_clown' schemaVersion = 1 quote = reference(doc=""" L{Quote} this participant is involved in. """, allowNone=False, reftype=Quote) nickname = text(doc=""" The participant's nickname. """, allowNone=False) LIMIT = 25 class QuoteDb(Item): typeName = 'flyingcircus_quotedb' schemaVersion = 2 powerupInterfaces = (IQuoteDatabase,) lastQid = integer(default=0) def addQuote(self, content): self.lastQid = self.lastQid + 1 quote = Quote(store=self.store, qid=self.lastQid, content=content, added=Time()) createParticipants(quote) return quote def getQuote(self, qid): return self.store.findUnique(Quote, Quote.qid == qid) def getRecentQuotes(self): return self.store.query(Quote, limit=LIMIT, sort=Quote.added.descending) def getTopQuotes(self): return self.store.query(Quote, limit=LIMIT, sort=Quote.rating.descending) def getBottomQuotes(self): return self.store.query(Quote, limit=LIMIT, sort=Quote.rating.ascending) def getRandomQuotes(self): oids = list(self.store.query(Quote).getColumn('storeID')) limit = min(len(oids), LIMIT) return (self.store.getItemByID(oid) for oid in random.sample(oids, limit)) def countQuotes(self): return self.store.query(Quote).count() def getQuotesByNicknames(self, nicknames): query = self.store.query(Clown, AND(Quote.storeID == Clown.quote, Clown.nickname.oneOf(nicknames)), sort=Quote.added.descending) nicknames = set(nicknames) for quote, clowns in groupby(query, lambda c: c.quote): if set(clown.nickname for clown in clowns) == nicknames: yield quote def qdb1to2(old): return old.upgradeVersion(QuoteDb.typeName, 1, 2, lastQid=old.lastQid) registerUpgrader(qdb1to2, QuoteDb.typeName, 1, 2)