def __init__(self, creator, server, name=None, description=None, openTime=None, closeTime=None, absoluteThreshold=None, percentThreshold=None, percentThresholdMinimum=None, thresholdTime=None, keepUpdated=True, pollid=None): self.log = Logger() self.base = CabbageBase() self.creator = creator self.server = server self.name = name self.description = description self.openTime = openTime self.closeTime = closeTime self.absoluteThreshold = absoluteThreshold self.percentThreshold = percentThreshold self.percentThresholdMinimum = percentThresholdMinimum self.thresholdTime = thresholdTime self.options = {'short': [], 'long': [], 'emoji': []} self.keepUpdated = keepUpdated if pollid: self.pollid = pollid else: self.genPollid() self.update()
class Logger: ''' Functions for writing log data to the SQL database ' Note on logging verbosity levels: ' 1 -- critical program error/crash ' 2 -- program error or warning ' 3 -- extra-high-importance ' 4 -- high-importance (e.g. admin promotion/demotion) ' 5 -- standard importance ' 6 -- low importance (new poll tracking messages) ' 7 -- extra-low importance ''' def __init__(self): self.base = CabbageBase() def log(self, message, module='Core', verbosity=5): ''' Logs a message (with optional module id) to the database ''' cmd = 'INSERT INTO log (time, message, module, verbosity) VALUES (%s,%s,%s,%s)' cur = self.base.getCursor() cur.execute(cmd, (datetime.now(), message, module, verbosity)) self.base.commit() cur.close()
def __init__(self, bot): self.bot = bot self.base = DB() self.saved = None
class Poll: ''' Polling module ''' def __init__(self, bot): self.bot = bot self.base = DB() self.saved = None async def on_reaction_add(self, reaction, user): if user == self.bot.user: return res = self.base.query('activemessages', ('pollid', ), (('messid', int(reaction.message.id)), )) if len(res) > 0: pollid = res[0][0] poll = PollFramework.fromSQL( self.base.query('polls', filters=(('pollid', int(pollid)), ))[0]) resp = poll.voteEmoji(user.id, reaction.emoji) await self.updatePoll(pollid) @commands.command(pass_context=True) async def react(self, ctx, *emojis): print(emojis) for emoji in emojis: if re.match('<:.+:[0-9]+>', emoji): # Custom emoji -- parse it as such await self.bot.add_reaction(ctx.message, emoji[1:-1]) else: # Maybe a unicode emoji -- try it, I guess await self.bot.add_reaction(ctx.message, emoji[0]) @commands.command(pass_context=True) async def saveme(self, ctx): p = Phrasebook(ctx, self.bot) await self.bot.say(p.pickPhrase('poll', 'saveme')) @commands.command(pass_context=True) async def wakemeup(self, ctx): p = Phrasebook(ctx, self.bot) await self.bot.say(p.pickPhrase('poll', 'wakemeup')) @commands.group(invoke_without_command=True) async def poll(self): await self.bot.say('Test') @poll.command(pass_context=True) async def test1(self, ctx): if ctx.message.server: f = PollFramework(ctx.message.author.id, ctx.message.server.id, ctx.message.channel.id, ctx.message.id) else: f = PollFramework(ctx.message.author.id, None, ctx.message.channel.id, ctx.message.id) f.setName('Motion to Test The Poll Module') f.setDescription( 'Approval or rejection of this motion will have tested the Poll module, so feel free to do either (or try to do both!)' ) f.setOpenTime(datetime.now()) f.setCloseTime(datetime.now() + timedelta(seconds=30)) f.setAbsoluteThreshold(10) f.setPercentThreshold(0.80) f.setPercentThresholdMinimum(5) f.setThresholdTime(datetime.now() + timedelta(seconds=10)) f.addOption('Support', 'Support the addition of the polling module', '✅') f.addOption('Oppose', 'Oppose the addition of the polling module', '❌') f.addOption('Woah', 'Woah', '<:woah:382379019993350145>') mess = await self.render(f) f.addTrackingMessage(mess.id, mess.channel.id) @poll.command(pass_context=True) async def create(self, ctx, name, description): if ctx.message.server: f = PollFramework(ctx.message.author.id, ctx.message.server.id, name, description) else: f = PollFramework(ctx.message.author.id, None, name, description) await self.bot.say('Created a new poll with id ' + str(f.get()['pollid'])) @poll.group() async def set(self): pass @set.command(pass_context=True) async def name(self, ctx, name, pollid=None): poll = await self.lookupPoll(ctx, pollid) if not poll: return poll.setName(name) await self.updatePoll(pollid) @set.command(pass_context=True) async def description(self, ctx, desc, pollid=None): poll = await self.lookupPoll(ctx, pollid) if not poll: return poll.setDescription(desc) await self.updatePoll(pollid) @set.command(pass_context=True) async def numThreshold(self, ctx, thresh, pollid=None): poll = await self.lookupPoll(ctx, pollid) if not poll: return poll.setAbsoluteThreshold(thresh) await self.updatePoll(pollid) @set.command(pass_context=True) async def percThreshold(self, ctx, thresh, minimum=1, pollid=None): poll = await self.lookupPoll(ctx, pollid) if not poll: return poll.setPercentThreshold(thresh) poll.setPercentThresholdMinimum(minimum) await self.updatePoll(pollid) @poll.command(pass_context=True) async def addResp(self, ctx, short, med, emoji=None, pollid=None): poll = await self.lookupPoll(ctx, pollid) if not poll: return poll.addOption(short, med, emoji) await self.updatePoll(pollid) @poll.command(pass_context=True) async def open(self, ctx, closeTime=1, closeIncrement='day', pollid=None): poll = await self.lookupPoll(ctx, pollid) if not poll: return now = datetime.now() closeIncrement = closeIncrement.lower() if closeIncrement == 'second' or closeIncrement == 'seconds': t = timedelta(seconds=int(closeTime)) elif closeIncrement == 'minute' or closeIncrement == 'minutes': t = timedelta(minutes=int(closeTime)) elif closeIncrement == 'hour' or closeIncrement == 'hours': t = timedelta(hours=int(closeTime)) elif closeIncrement == 'day' or closeIncrement == 'days': t = timedelta(days=int(closeTime)) elif closeIncrement == 'week' or closeIncrement == 'weeks': t = timedelta(weeks=int(closeTime)) elif closeIncrement == 'month' or closeIncrement == 'months': t = timedelta(months=int(closeTime)) elif closeIncrement == 'year' or closeIncrement == 'years': t = timedelta(years=int(closeTime)) else: await self.bot.say('Unknown time interval "' + closeIncrement + '"') closeTime = now + t poll.setOpenTime(now) poll.setCloseTime(closeTime) await self.updatePoll(pollid) print('test') @poll.command(pass_context=True) async def vote(self, ctx, vote, pollid=None): if not pollid: res = await self.autoGuessPollid(ctx) if res: pollid = res else: return poll = PollFramework.fromSQL( self.base.query('polls', filters=(('pollid', int(pollid)), ))[0]) toDel = None resp = poll.vote(ctx.message.author.id, vote) if resp == 0: toDel = await self.bot.say('Successfully voted on poll ID ' + str(pollid)) elif resp == 1: toDel = await self.bot.say('Invalid option "' + vote + '" for poll ID ' + str(pollid) + '!') elif resp == 2: toDel = await self.bot.say('Poll ID ' + str(pollid) + ' is not open!') mark = datetime.now() await self.updatePoll(pollid) while datetime.now() - mark < timedelta(seconds=5): pass await self.bot.delete_message(toDel) await self.bot.delete_message(ctx.message) @poll.command() async def put(self, pollid): poll = PollFramework.fromSQL( self.base.query('polls', filters=(('pollid', int(pollid)), ))[0]) mess = await self.render(poll) poll.addTrackingMessage(mess.id, mess.channel.id) @poll.command(pass_context=True) async def update(self, ctx, single=None): await self.updatePoll(single) async def updatePoll(self, single=None): if single: res = self.base.query('activemessages', ('messid', 'chanid', 'pollid'), (('pollid', int(single)), )) else: res = self.base.query('activemessages', ('messid', 'chanid', 'pollid')) for active in res: print('Attempting to update poll with id ' + str(active[2])) try: chan = self.bot.get_channel(str(active[1])) if not chan: raise TypeError mess = await self.bot.get_message(chan, str(active[0])) poll = PollFramework.fromSQL( self.base.query('polls', filters=(('pollid', active[2]), ))[0]) await self.render(poll, mess) except (discord.errors.NotFound, TypeError): rc.pwarn('Message id ' + str(active[0]) + ' on channel id ' + str(active[1]) + ' no longer valid! Deleting.') cur = self.base.getCursor() cmd = 'DELETE FROM activemessages WHERE messid=%s AND chanid=%s' cur.execute(cmd, (active[0], active[1])) self.base.commit() cur.close() async def lookupPoll(self, ctx, pollid=None): if not pollid: pollid = await self.autoGuessPollid(ctx) if not pollid: return None poll = PollFramework.fromSQL( self.base.query('polls', filters=(('pollid', int(pollid)), ))[0]) return poll async def autoGuessPollid(self, ctx): res = self.guessPollid(ctx) if res[0] == 0: return res[1] elif res[1] == 1: await self.bot.say( 'No polls active in this channel. Please specify a pollid.') return None else: await self.bot.say( 'More than one poll active in this channel. Please specify a pollid.' ) return None def guessPollid(self, ctx): res = self.base.query('activemessages', ('pollid', ), (('chanid', int(ctx.message.channel.id)), )) if len(res) == 0: return (1, None) # No polls active in this channel elif len(res) == 1: return (0, res[0][0]) else: return (2, None) # More than one poll active in the channel async def render(self, framework, existingMess=None): res = framework.get() s = 'Poll ID ' + str(res['pollid']) + '\n' s = s + '**' + res['name'] + '**\n' + res['description'] + '\n\n' opened = False final = False if res['openTime'] > datetime.now(): # Poll is not open yet s = s + 'This poll is **not open yet**! It will open on ' + res['openTime'].strftime('%A, %d %B %Y at %H:%M:%S')\ + ' and close on ' + res['closeTime'].strftime('%A, %d %B %Y at %H:%M:%S') + '.' elif res['closeTime'] < datetime.now(): # Poll has already closed opened = True final = True s = s + 'This poll is **closed**! It opened on ' + res['openTime'].strftime('%A, %d %B %Y at %H:%M:%S')\ + ' and closed on ' + res['closeTime'].strftime('%A, %d %B %Y at %H:%M:%S') + '.' else: # Poll is open opened = True s = s + 'This poll is **open**! It has been open since ' + res['openTime'].strftime('%A, %d %B %Y at %H:%M:%S')\ + ' and will close on ' + res['closeTime'].strftime('%A, %d %B %Y at %H:%M:%S') + '.' # Render options s = s + '\n\n**Accepted responses**' for dex, option in enumerate(res['options']['short']): s = s + '\n\n**' + option + '**' emoj = None if res['options']['emoji'][dex]: emoj = self.typeEmoji(res['options']['emoji'][dex]) s = s + ' (' + emoj + ')' s = s + ': ' + res['options']['long'][ dex] + '\n' + self.genCmdString(option, emoj) if opened: s = s + '\n\n**' if final: s = s + 'Final ' else: s = s + 'Current ' s = s + 'Vote Totals**\n' # Render votes votes = framework.getVoteTotals() s = s + '```\n' scale = self.getScale(votes) scaleFactor = scale[0] padLength = scale[1] for key, vote in votes.items(): s = s + self.pad(key, padLength) + ' ' + str(vote) + '|' if scaleFactor > 0: for i in range(0, int(vote * scaleFactor)): s = s + '#' s = s + '\n' s = s + '```' if existingMess: await self.bot.edit_message(existingMess, s) else: existingMess = await self.bot.say(s) for em in res['options']['emoji']: if em: await self.bot.add_reaction(existingMess, em) return existingMess def pad(self, toPad, padLength): while len(toPad) < padLength: toPad = toPad + ' ' return toPad def getScale(self, votes): maxVote = 0 longestShortString = 0 scaleFactor = 1 for vote in votes.values(): if vote > maxVote: maxVote = vote voteDigits = 0 if maxVote > 0: voteDigits = int(math.log10(maxVote)) + 1 for shortString in votes.keys(): if len(shortString) > longestShortString: longestShortString = len(shortString) while maxVote * scaleFactor + voteDigits + longestShortString + 2 > 80: scaleFactor = scaleFactor - 0.1 return (scaleFactor, longestShortString) def genCmdString(self, option, emoji): s = '(run ' + rc.PREF + 'poll vote ' + option if emoji: s = s + ' or click the reaction button below' s = s + ' to vote for this option)' return s def typeEmoji(self, emoji): emojiRx = re.compile('(:.+:)[0-9]+') res = emojiRx.match(emoji) if res: return res.group(1) else: return emoji
def __init__(self, cmdCtx, bot): self.base = CabbageBase() self.cmdCtx = cmdCtx self.bot = bot random.seed()
class Phrasebook: def __init__(self, cmdCtx, bot): self.base = CabbageBase() self.cmdCtx = cmdCtx self.bot = bot random.seed() def phrasebookScan(self, mod, ctx): ''' Scan the phrasebook for the given context and return all results ''' scRes = self.base.query2Filter('phrasebook', 'module', mod, 'context', ctx) phrases = [] for res in scRes: phrases.append(res[2]) return phrases def pickPhraseRaw(self, mod, ctx): ''' Randomly select a phrase from the given context; do not perform substitution ''' return random.choice(self.phrasebookScan(mod, ctx)) def pickPhrase(self, mod, ctx, *customSubs): ''' Randomly select a phrase from the given context, and perform % substitutions ''' return self.doSubstitutions(self.pickPhraseRaw(mod, ctx), customSubs) def doSubstitutions(self, subs, *cSubs): ''' Performs % subsitutions on the given string ''' modSt = '' isEscaped = False for c, actual in enumerate(subs): if subs[c] == '%': isEscaped = True elif not isEscaped: modSt += (subs[c]) else: isEscaped = False if subs[c] == '%': modSt += ('%') elif subs[c] == 'u': modSt += (self.cmdCtx.message.author.name) elif subs[c] == 'U': modSt += (self.cmdCtx.message.author.mention) elif subs[c] == 'm': modSt += (self.bot.user.name) elif subs[c] == 'E': modSt += ('@everyone') elif subs[c] == 't': modSt += (datetime.now().strftime('%H:%M:%S')) elif subs[c] == 'T': modSt += ( datetime.now().strftime('%H:%M:%S on %A, %d %B, %Y')) elif subs[c] == 'i': modSt += (rc.PREF) else: try: intSub = int(subs[c]) except ValueError: pass else: if intSub == 0: intSub = 10 if len(cSubs) > 0 and intSub <= len(cSubs[0]): modSt += str(cSubs[0][intSub - 1]) return modSt
def __init__(self): self.base = CabbageBase()
class FlagFramework: ''' Functions for managing simple flags in the SQL database ' Note that flags are not unique -- that is, it is possible to have ' multiple flags with the same name but different values. ''' def __init__(self): self.base = CabbageBase() self.tables = [ 'baseflags', 'boolflags', 'textflags', 'intflags', 'userflags' ] self.ttypes = [None, bool, str, int, int] def fset(self, flag, module, server=0): ''' Sets a flag without a value ''' cmd = 'INSERT INTO baseFlags (time, name, module, server) VALUES (%s,%s,%s,%s)' cur = self.base.getCursor() cur.execute(cmd, (datetime.now(), flag, module, int(server))) self.base.commit() cur.close() def bset(self, flag, module, server=0, value=True): ''' Sets a flag with a boolean value ''' cmd = 'INSERT INTO boolFlags (time, name, module, flag, server) VALUES (%s,%s,%s,%s,%s)' cur = self.base.getCursor() cur.execute(cmd, (datetime.now(), flag, module, value, int(server))) self.base.commit() cur.close() def tset(self, flag, module, server=0, value=''): ''' Sets a flag with a plaintext value ''' cmd = 'INSERT INTO textFlags (time, name, module, flag, server) VALUES (%s,%s,%s,%s,%s)' cur = self.base.getCursor() cur.execute(cmd, (datetime.now(), flag, module, value, int(server))) self.base.commit() cur.close() def iset(self, flag, module, server=0, value=0): ''' Sets a flag with an integer value ''' cmd = 'INSERT INTO intFlags (time, name, module, flag, server) VALUES (%s,%s,%s,%s,%s)' cur = self.base.getCursor() cur.execute(cmd, (datetime.now(), flag, module, value, int(server))) self.base.commit() cur.close() def uset(self, flag, module, server, value): ''' Sets a flag with a discord user value ''' cmd = 'INSERT INTO userFlags (time, name, module, flag, server) VALUES (%s,%s,%s,%s,%s)' cur = self.base.getCursor() cur.execute(cmd, (datetime.now(), flag, module, value, int(server))) self.base.commit() cur.close() def hasFlag(self, name, module, server=0): ''' Checks if the given flag exists, regardless of type or value ''' for table in self.tables: if self.base.isPresentIn('name', name, table, (('module', module), ('server', int(server)))): return True return False def getFlag(self, name, module, server=0): ''' Gets all present values for the given flag ''' flags = [] for table in self.tables: if table == 'baseflags': query = self.base.query(table, ('time', 'name', 'module', 'server'), (('name', name), ('module', module), ('server', int(server)))) if query and len(query) > 0: for que in query: flags.append({ 'timestamp': que[0], 'name': que[1], 'module': que[2], 'flag': None, 'server': que[3] }) else: query = self.base.query( table, ('time', 'name', 'module', 'flag', 'server'), (('name', name), ('module', module), ('server', int(server)))) if query and len(query) > 0: for que in query: flags.append({ 'timestamp': que[0], 'name': que[1], 'module': que[2], 'flag': que[3], 'server': que[4] }) if len(flags) > 0: return flags else: return None def delFlag(self, name, module, server=0, value=None): ''' Removes a flag from the database.''' for dex, table in enumerate(self.tables): if value and type(value) != self.ttypes[dex]: ''' This table can't possibly have the value. Skip it.''' continue cmd = 'DELETE FROM {} WHERE name = %s AND module = %s AND server = %s' if value and table != 'baseflags': cmd = cmd + ' AND flag = %s' cmd = cmd + ';' cur = self.base.getCursor() print('Removing flags with value ' + str(value) + ' from server id ' + str(server)) if value and table != 'baseflags': cur.execute( sql.SQL(cmd).format(sql.Identifier(table)), (name, module, server, value)) else: cur.execute( sql.SQL(cmd).format(sql.Identifier(table)), (name, module, server)) self.base.commit() cur.close()
def __init__(self): self.base = CabbageBase() self.tables = [ 'baseflags', 'boolflags', 'textflags', 'intflags', 'userflags' ] self.ttypes = [None, bool, str, int, int]
class PollFramework: ''' Backend tasks for polls ''' def __init__(self, creator, server, name=None, description=None, openTime=None, closeTime=None, absoluteThreshold=None, percentThreshold=None, percentThresholdMinimum=None, thresholdTime=None, keepUpdated=True, pollid=None): self.log = Logger() self.base = CabbageBase() self.creator = creator self.server = server self.name = name self.description = description self.openTime = openTime self.closeTime = closeTime self.absoluteThreshold = absoluteThreshold self.percentThreshold = percentThreshold self.percentThresholdMinimum = percentThresholdMinimum self.thresholdTime = thresholdTime self.options = {'short': [], 'long': [], 'emoji': []} self.keepUpdated = keepUpdated if pollid: self.pollid = pollid else: self.genPollid() self.update() @classmethod def fromSQL(cls, queryRow): log = Logger() log.log('Building pollid ' + str(queryRow[0]) + ' from SQL row', 'poll', 8) working = cls(queryRow[1], queryRow[2], queryRow[3], queryRow[4], queryRow[5], queryRow[6], queryRow[7], queryRow[8], queryRow[9], queryRow[10], keepUpdated=False, pollid=queryRow[0]) opts = [] for i in range(0, len(queryRow[11])): working.addOption(queryRow[11][i], queryRow[12][i], queryRow[13][i]) working.setUpdate(True) print('Built pollid ' + str(queryRow[0])) return working def genPollid(self): #while True: propPollid = random.randint(-1 * sys.maxsize, sys.maxsize) # This checks for the infinitesimal (1/(2*sys.maxsize+1) ~= 0) chance # of an id conflict. Uncomment if it ever becomes a problem. It shouldn't. # if not self.base.isPresentIn('pollid', propPollid, 'polls'): # break self.pollid = propPollid self.update() def setName(self, name): self.name = name self.update() def setDescription(self, desc): self.description = desc self.update() def setOpenTime(self, openTime): self.openTime = openTime self.update() def setCloseTime(self, closeTime): self.closeTime = closeTime self.update() def setAbsoluteThreshold(self, absThreshold): self.absoluteThreshold = absThreshold self.update() def setPercentThreshold(self, percThreshold): self.percentThreshold = percThreshold self.update() def setPercentThresholdMinimum(self, percThresholdMin): self.percentThresholdMinimum = percThresholdMin self.update() def setThresholdTime(self, threshTime): self.thresholdTime = threshTime self.update() def setUpdate(self, keepUp): self.keepUpdated = keepUp def addOption(self, shortOption, longOption, emojiOption=None): self.options['short'].append(shortOption) self.options['long'].append(longOption) if emojiOption: if re.match('<:.+:[0-9]+>', emojiOption): self.options['emoji'].append(emojiOption[-1:1]) else: self.options['emoji'].append(emojiOption[0]) else: self.options['emoji'].append(None) self.update() def addTrackingMessage(self, messid, chanid): ''' Add a message to the list of tracked messages ''' res = self.base.query('activemessages', filters=(('messid', int(messid)), ('chanid', int(chanid)))) if res and len(res) > 0: # This message is already registered return False cmd = 'INSERT INTO activemessages (messid, chanid, pollid) VALUES (%s,%s,%s)' cur = self.base.getCursor() cur.execute(cmd, (int(messid), int(chanid), self.pollid)) self.base.commit() cur.close() return True def delTrackingMessage(self, messid, chanid): ''' Remove a message from the list of tracked messages ''' res = self.base.query('activemessages', filters=(('messid', int(messid)), ('chanid', int(chanid)))) if not res or len(res) == 0: # Message not found return False cmd = 'DELETE FROM ONLY activemessages WHERE messid = %s AND chanid = %s' cur = self.base.getCursor() cur.execute(cmd, (messid, chanid)) self.base.commit() cur.close() return True def getTrackingMessages(self): ''' Return all messages that are supposed to be updated ''' res = self.base.query('activemessages', ('messid', 'chanid', 'pollid')) processed = [] for result in res: # Convert to dict for convenience processed.append({ 'messid': result[0], 'chanid': result[1], 'pollid': result[2] }) return processed def get(self): ''' Return a dictionary containing all of the relevant poll parameters (i.e. everything except votes and tracking messages) ''' return { 'creator': self.creator, 'server': self.server, 'name': self.name, 'description': self.description, 'pollid': self.pollid, 'openTime': self.openTime, 'closeTime': self.closeTime, 'absoluteThreshold': self.absoluteThreshold, 'percentThreshold': self.percentThreshold, 'percentThresholdMinimum': self.percentThresholdMinimum, 'thresholdTime': self.thresholdTime, 'options': self.options } def vote(self, user, shortOption): ''' Registers a vote from a user by short option ''' res = self.base.query('polls', ('shortoptions', 'opentime', 'closetime'), (('pollid', self.pollid), )) curTime = datetime.now() if curTime - res[0][1] < timedelta( seconds=0) or curTime - res[0][2] > timedelta(seconds=0): return 2 # Poll is closed cmd = 'DELETE FROM ONLY votes WHERE voterid = %s AND pollid = %s;' cur = self.base.getCursor() cur.execute(cmd, (user, self.pollid)) if shortOption not in res[0][0]: self.base.rollback() cur.close() return 1 # Invalid option cmd = 'INSERT INTO votes (voterid, pollid, voteTime, vote) VALUES (%s,%s,%s,%s)' cur.execute(cmd, (user, self.pollid, datetime.now(), shortOption)) self.base.commit() cur.close() return 0 # OK def voteEmoji(self, user, emojiOption): ''' Registers a votefrom a user by emoji option ''' cmd = 'DELETE FROM ONLY votes WHERE voterid = %s AND pollid = %s;' cur = self.base.getCursor() cur.execute(cmd, (user, self.pollid)) res = self.base.query('polls', ('shortoptions', 'emojioptions'), (('pollid', self.pollid), )) shortEq = None for dex, shortOpt in enumerate(res[0][0]): if res[0][1][dex] == emojiOption: shortEq = shortOpt if not shortEq: # User voted with invalid emoji. Bad user. self.base.rollback() cur.close() return 1 return self.vote(user, shortEq) def getVoteDetails(self): ''' Retrieves all votes from the SQL database ''' return self.base.query('votes', ('voterid', 'pollid', 'votetime', 'vote'), (('pollid', self.pollid), )) def getVoteTotals(self): ''' Retrieves the total number of votes for each option ''' counts = {} cur = self.base.getCursor() for option in self.options['short']: cur.execute('SELECT * FROM votes WHERE pollid = %s AND vote = %s', (self.pollid, option)) counts[option] = cur.rowcount cur.close() return counts def update(self): ''' Update the SQL database to reflect changes to the object ''' if not self.keepUpdated: return # Update only functions if the object has keepUpdated set true self.log.log( 'Poll ID ' + str(self.pollid) + ' (' + self.name + ') updated.', 'poll', 7) cur = self.base.getCursor() table = 'polls' execString = '(creatorid, serverid, name, description, pollid, openTime, closeTime, absoluteThreshold, percentThreshold, percentThresholdMinimum, thresholdTime, shortOptions, longOptions, emojiOptions)' valString = '(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)' caveat = 'WHERE pollid=' + str(self.pollid) constructed = 'UPDATE ONLY ' + table + ' SET ' + execString + ' = ' + valString + caveat + ';' print(str(cur.mogrify( \ constructed, \ (self.creator, \ self.server, \ self.name, \ self.description, \ self.pollid, \ self.openTime, \ self.closeTime, \ self.absoluteThreshold, \ self.percentThreshold, \ self.percentThresholdMinimum, \ self.thresholdTime, \ self.options['short'], \ self.options['long'], \ self.options['emoji'] \ )\ ))) cur.execute( \ constructed, \ (self.creator, \ self.server, \ self.name, \ self.description, \ self.pollid, \ self.openTime, \ self.closeTime, \ self.absoluteThreshold, \ self.percentThreshold, \ self.percentThresholdMinimum, \ self.thresholdTime, \ self.options['short'], \ self.options['long'], \ self.options['emoji'] \ )\ ) if cur.statusmessage == 'UPDATE 0': # This is a new poll; insert it into the table constructed = 'INSERT INTO ' + table + execString + ' VALUES ' + valString + ';' cur.execute( \ constructed, \ (self.creator, \ self.server, \ self.name, \ self.description, \ self.pollid, \ self.openTime, \ self.closeTime, \ self.absoluteThreshold, \ self.percentThreshold, \ self.percentThresholdMinimum, \ self.thresholdTime, \ self.options['short'], \ self.options['long'], \ self.options['emoji'] \ )\ ) ms = 'Created new poll with pollid ' + str(self.pollid) rc.pinfo(ms) self.log.log(ms, 'poll', 6) self.base.commit() elif cur.statusmessage != 'UPDATE 1': errm = 'Rolling back UPDATE command for Poll object due to abnormal response -- check for multiple polls with the same pollid: ' + str( self.pollid) + ' (returned message: ' + str( cur.statusmessage) + ')' rc.perr(errm) self.log.log(errm, 'poll', 2) self.base.rollback() else: # Normal response; safe to commit self.base.commit() # Either way, release the database hold cur.close()