""" Karma - handle karma (++ and --) tracking Copyright (C) 2010 Brian S. Stephan This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ from math import floor import re import MySQLdb as mdb from Module import Module __author__ = "Mike Bloy " __date__ = "$Oct 23, 2010 11:12:33 AM$" class Karma(Module): def __init__(self, irc, config): """ Upon creation, determine the save file location """ Module.__init__(self, irc, config) pattern = "(?:\((.+?)\)|(\S+))" karmapattern = pattern + '(\+\+|--|\+-|-\+)' + '(\s+|$)' querypattern = '^!rank\s+(.*)' reportpattern = '^!karma\s+report\s+(highest|lowest|positive|negative|top)' statpattern = '^!karma\s+stat\s+(.*)' self.karmare = re.compile(karmapattern) self.queryre = re.compile(querypattern) self.reportre = re.compile(reportpattern) self.statre = re.compile(statpattern) def db_init(self): # need to init the database if karma tables don't already exist version = self.db_module_registered(self.__class__.__name__) if (version == None): # have to create the database tables db = self.get_db() try: version = 1 cur = db.cursor(mdb.cursors.DictCursor) cur.execute(''' CREATE TABLE karma_log ( id SERIAL, karma_key VARCHAR(128) NOT NULL, delta INTEGER NOT NULL, who VARCHAR(64) NOT NULL, userhost VARCHAR(256) NOT NULL, karmatime TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin ''') cur.execute('CREATE INDEX karma_log_key_ix ON karma_log (karma_key)') cur.execute('CREATE INDEX karma_log_who_ix ON karma_log (who)') cur.execute(''' CREATE VIEW karma_values AS SELECT karma_key, SUM(delta) AS value FROM karma_log GROUP BY karma_key''') cur.execute(''' CREATE VIEW karma_users AS SELECT who, COUNT(NULLIF(delta, -1)) AS pos, COUNT(NULLIF(delta, 1)) AS neg FROM karma_log GROUP BY who''') db.commit() self.db_register_module_version(self.__class__.__name__, version) except mdb.Error as e: db.rollback() self.log.error("database error trying to create tables") self.log.exception(e) raise finally: cur.close() def do(self, connection, event, nick, userhost, what, admin_unlocked): """look for karma strings at the start of messages""" # don't return anything, do this attempt regardless if (self.karmare.search(what)): self.handle_karma_change(connection, nick, userhost, what) if (self.queryre.search(what)): return self.irc.reply(event, self.handle_karma_query(connection, nick, userhost, what)) elif (self.statre.search(what)): return self.irc.reply(event, self.handle_stat_query(connection, nick, userhost, what)) elif (self.reportre.search(what)): return self.irc.reply(event, self.handle_report_query(connection, nick, userhost, what)) def handle_karma_change(self, connection, nick, userhost, what): """ handle the karma change and storage. """ if self.karmare.search(what): matches = self.karmare.findall(what) for match in matches: key = match[0] if match[0] else match[1] value = match[2] if (value == '++'): self.karma_modify(key, 1, nick, userhost) elif (value == '--'): self.karma_modify(key, -1, nick, userhost) elif (value == '+-'): self.karma_modify(key, 1, nick, userhost) self.karma_modify(key, -1, nick, userhost) elif (value == '-+'): self.karma_modify(key, -1, nick, userhost) self.karma_modify(key, 1, nick, userhost) def karma_modify(self, key, value, nick, userhost): """ Go out to the database and update the karma value. """ db = self.get_db() try: cur = db.cursor(mdb.cursors.DictCursor) sql = ''' INSERT INTO karma_log (karma_key, delta, who, userhost) VALUES (%s, %s, %s, %s) ''' cur.execute(sql, (key.lower(), value, nick, userhost)) db.commit() except mdb.Error as e: db.rollback() self.log.error("database error modifying karma") self.log.exception(e) raise finally: cur.close() def handle_report_query(self, connection, nick, userhost, what): match = self.reportre.search(what) report = match.group(1) message = '{nick}: the desired report is not yet implemented'.format(nick=nick) query = None header = None if (report == 'highest'): query = 'SELECT karma_key AS who, value FROM karma_values ORDER BY value DESC LIMIT 5' header = 'Top 5 karma recipients:' elif (report == 'lowest'): query = 'SELECT karma_key AS who, value FROM karma_values ORDER BY value ASC LIMIT 5' header = 'Bottom 5 karma recipients:' elif (report == 'positive'): query = 'SELECT who, pos AS value FROM karma_users ORDER BY pos DESC LIMIT 5' header = 'Top 5 Optimists:' elif (report == 'negative'): query = 'SELECT who, neg AS value FROM karma_users ORDER BY neg DESC LIMIT 5' header = 'Top 5 Pessimists:' elif (report == 'top'): query = 'SELECT who, pos+neg AS value FROM karma_users ORDER BY value DESC LIMIT 5' header = 'Top 5 Total Karma Givers:' if (query != None): db = self.get_db() list = [] try: cur = db.cursor(mdb.cursors.DictCursor) cur.execute(query) result = cur.fetchone() while (result != None): list.append("{key} ({value})".format(key=result['who'], value=result['value'])) result = cur.fetchone() list = ', '.join(list) message = '{header} {list}'.format(header=header, list=list) except mdb.Error as e: self.log.error("database error during report query") self.log.exception(e) raise finally: cur.close() return message def handle_stat_query(self, connection, nick, userhost, what): match = self.statre.search(what) statnick = match.group(1) db = self.get_db() reply = '{nick}: {statnick} has never given karma'.format(nick=nick, statnick=statnick) try: cur = db.cursor(mdb.cursors.DictCursor) query = ''' SELECT pos, neg FROM karma_users WHERE who = :who ''' cur.execute(query, {'who': statnick}) value = cur.fetchone() if (value != None): pos = value[0] neg = value[1] total = pos+neg; reply = '{nick}: {statnick} has given {pos} postive karma and {neg} negative karma, for a total of {total} karma'.format(nick=nick, statnick=statnick, pos=pos, neg=neg, total=total) except mdb.Error as e: self.log.error("database error during handle stat query") self.log.exception(e) raise finally: cur.close() return reply def handle_karma_query(self, connection, nick, userhost, what): match = self.queryre.search(what) key = match.group(1) db = self.get_db() reply = '{nick}: {key} has no karma'.format(nick=nick, key=key) try: cur = db.cursor(mdb.cursors.DictCursor) query = ''' SELECT value FROM karma_values WHERE karma_key = %s ''' cur.execute(query, (key.lower(),)) value = cur.fetchone() if (value != None): query = ''' SELECT count(*) FROM karma_values WHERE value > %s ''' cur.execute(query, (value['value'],)) rank = cur.fetchone() rank = rank['count(*)'] + 1; reply = '{0:s}: {1:s} has {2:d} points of karma (rank {3:d})'.format( nick, key, int(floor(value['value'])), rank) except mdb.Error as e: self.log.error("database error during handle karma query") self.log.exception(e) raise finally: cur.close() return reply if __name__ == "__main__": print "Hello World" # vi:tabstop=4:expandtab:autoindent