Source code for cslbot.commands.vote

# -*- coding: utf-8 -*-
# Copyright (C) 2013-2015 Samuel Damashek, Peter Foley, James Forcier, Srijay Kasturi, Reed Koser, Christopher Reffett, and Fox Wilson
#
# 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 2
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import collections

from ..helpers import arguments
from ..helpers.command import Command
from ..helpers.orm import Poll_responses, Polls


[docs]def start_poll(args): """ Starts a poll """ if args.type == 'privmsg': return "We don't have secret ballots in this benevolent dictatorship!" if not args.msg: return "Polls need a question." ctrlchan = args.config['core']['ctrlchan'] poll = Polls(question=args.msg, submitter=args.nick) args.session.add(poll) args.session.flush() if args.isadmin: poll.accepted = 1 return "Poll #%d created!" % poll.id else: args.send("Poll submitted for approval.", target=args.nick) args.send("New Poll: #%d -- %s, Submitted by %s" % (poll.id, args.msg, args.nick), target=ctrlchan) return ""
[docs]def delete_poll(args): """ Deletes a poll """ if not args.isadmin: return "Nope, not gonna do it." if not args.msg: return "Syntax: !poll delete <pollnum>" if not args.msg.isdigit(): return "Not A Valid Positive Integer." poll = args.session.query(Polls).filter(Polls.accepted == 1, Polls.id == int(args.msg)).first() if poll is None: return "Poll does not exist." if poll.active == 1: return "You can't delete an active poll!" elif poll.deleted == 1: return "Poll already deleted." poll.deleted = 1 return "Poll deleted."
[docs]def get_open_poll(session, pid): return session.query(Polls).filter(Polls.deleted == 0, Polls.accepted == 1, Polls.id == pid).first()
[docs]def edit_poll(args): """ Edits a poll """ if not args.isadmin: return "Nope, not gonna do it." msg = args.msg.split(maxsplit=1) if len(msg) < 2: return "Syntax: !vote edit <pollnum> <question>" if not msg[0].isdigit(): return "Not A Valid Positive Integer." pid = int(msg[0]) poll = get_open_poll(args.session, pid) if poll is None: return "That poll was deleted or does not exist!" poll.question = msg[1] return "Poll updated!"
[docs]def reopen(args): """ reopens a closed poll.""" if not args.isadmin: return "Nope, not gonna do it." msg = args.msg.split() if not msg: return "Syntax: !poll reopen <pollnum>" if not msg[0].isdigit(): return "Not a valid positve integer." pid = int(msg[0]) poll = get_open_poll(args.session, pid) if poll is None: return "That poll doesn't exist or has been deleted!" poll.active = 1 return "Poll %d reopened!" % pid
[docs]def end_poll(args): """ Ends a poll """ if not args.isadmin: return "Nope, not gonna do it." if not args.msg: return "Syntax: !vote end <pollnum>" if not args.msg.isdigit(): return "Not A Valid Positive Integer." poll = get_open_poll(args.session, int(args.msg)) if poll is None: return "That poll doesn't exist or has already been deleted!" if poll.active == 0: return "Poll already ended!" poll.active = 0 return "Poll ended!"
[docs]def tally_poll(args): """Shows the results of poll """ if not args.msg: return "Syntax: !vote tally <pollnum>" if not args.msg.isdigit(): return "Not A Valid Positive Integer." pid = int(args.msg) poll = get_open_poll(args.session, pid) if poll is None: return "That poll doesn't exist or was deleted. Use !poll list to see valid polls" state = "Active" if poll.active == 1 else "Closed" votes = args.session.query(Poll_responses).filter(Poll_responses.pid == pid).all() args.send("%s poll: %s, %d total votes" % (state, poll.question, len(votes))) votemap = collections.defaultdict(list) for v in votes: votemap[v.response].append(v.voter) for x in sorted(votemap.keys()): args.send("%s: %d -- %s" % (x, len(votemap[x]), ", ".join(votemap[x])), target=args.nick) if not votemap: return "" ranking = collections.defaultdict(list) for x in votemap.keys(): num = len(votemap[x]) ranking[num].append(x) high = max(ranking) winners = (ranking[high], high) if len(winners[0]) == 1: winners = (winners[0][0], high) return "The winner is %s with %d votes." % winners else: winners = (", ".join(winners[0]), high) return "Tie between %s with %d votes." % winners
[docs]def get_response(session, pid, nick): return session.query(Poll_responses).filter(Poll_responses.pid == pid, Poll_responses.voter == nick).first()
[docs]def vote(session, nick, pid, response): """ Votes on a poll""" if not response: return "You have to vote something!" if response == "n" or response == "nay": response = "no" elif response == "y" or response == "aye": response = "yes" poll = get_open_poll(session, pid) if poll is None: return "That poll doesn't exist or isn't active. Use !poll list to see valid polls" old_vote = get_response(session, pid, nick) if old_vote is None: session.add(Poll_responses(pid=pid, response=response, voter=nick)) return "%s voted %s." % (nick, response) else: if response == old_vote.response: return "You've already voted %s." % response else: msg = "%s changed his/her vote from %s to %s." % (nick, old_vote.response, response) old_vote.response = response return msg
[docs]def retract(args): """ Deletes a vote for a poll """ if not args.msg: return "Syntax: !vote retract <pollnum>" if not args.msg.isdigit(): return "Not A Valid Positive Integer." response = get_response(args.session, args.msg, args.nick) if response is None: return "You haven't voted on that poll yet!" args.session.delete(response) return "Vote retracted"
[docs]def list_polls(args): num = args.session.query(Polls).filter(Polls.active == 1).count() return "There are %d polls. Check them out at %spolls.html" % (num, args.config['core']['url'])
@Command(['vote', 'poll'], ['db', 'nick', 'is_admin', 'type', 'config'])
[docs]def cmd(send, msg, args): """Handles voting. Syntax: {command} <start|end|list|tally|edit|delete|retract|reopen|(num) vote> """ command = msg.split() msg = " ".join(command[1:]) if not command: send("Which poll?") return else: command = command[0] # FIXME: integrate this with ArgParser if command.isdigit(): if args['type'] == 'privmsg': send("We don't have secret ballots in this benevolent dictatorship!") else: send(vote(args['db'], args['nick'], int(command), msg)) return isadmin = args['is_admin'](args['nick']) parser = arguments.ArgParser(args['config']) parser.set_defaults(session=args['db'], msg=msg, nick=args['nick']) subparser = parser.add_subparsers() start_parser = subparser.add_parser('start', config=args['config'], aliases=['open', 'add', 'create']) start_parser.set_defaults(func=start_poll, send=send, isadmin=isadmin, type=args['type']) tally_parser = subparser.add_parser('tally') tally_parser.set_defaults(func=tally_poll, send=send) list_parser = subparser.add_parser('list', config=args['config']) list_parser.set_defaults(func=list_polls) retract_parser = subparser.add_parser('retract') retract_parser.set_defaults(func=retract) end_parser = subparser.add_parser('end', aliases=['close']) end_parser.set_defaults(func=end_poll, isadmin=isadmin) delete_parser = subparser.add_parser('delete') delete_parser.set_defaults(func=delete_poll, isadmin=isadmin) edit_parser = subparser.add_parser('edit') edit_parser.set_defaults(func=edit_poll, isadmin=isadmin) reopen_parser = subparser.add_parser('reopen') reopen_parser.set_defaults(func=reopen, isadmin=isadmin) try: cmdargs = parser.parse_args(command) except arguments.ArgumentException as e: send(str(e)) return send(cmdargs.func(cmdargs))