def test_unique_ordering(self, session, user, poll): option = PollOption(poll, 'option 0') session.add(option) vote = Vote(user, option) vote.priority = 0 session.add(vote) with pytest.raises(IntegrityError): vote_same_index = Vote(user, option) vote_same_index.priority = 0 session.add(vote_same_index) session.commit()
def handle_single_vote(session, context, option): """Handle a single vote.""" locale = option.poll.locale existing_vote = session.query(Vote) \ .filter(Vote.poll == option.poll) \ .filter(Vote.user == context.user) \ .one_or_none() # Changed vote if existing_vote and existing_vote.poll_option != option: existing_vote.poll_option = option vote_changed = i18n.t('callback.vote.changed', locale=locale) respond_to_vote(session, vote_changed, context, option.poll) # Voted for the same thing again elif existing_vote and existing_vote.poll_option == option: session.delete(existing_vote) vote_removed = i18n.t('callback.vote.removed', locale=locale) context.query.answer(vote_removed) # First vote on this poll elif existing_vote is None: vote = Vote(context.user, option) session.add(vote) vote_registered = i18n.t('callback.vote.registered', locale=locale) respond_to_vote(session, vote_registered, context, option.poll) return True
def handle_doodle_vote(session, context, option): """Handle a doodle vote.""" locale = option.poll.locale vote = session.query(Vote) \ .filter(Vote.poll_option == option) \ .filter(Vote.user == context.user) \ .one_or_none() if context.callback_result.name is None: data = context.data # noqa raise Exception("Unknown callback result") # Remove vote if vote is not None: vote.type = context.callback_result.name changed = i18n.t('callback.vote.doodle_changed', locale=locale, vote_type=vote.type) context.query.answer(changed) # Add vote else: vote = Vote(context.user, option) vote.type = context.callback_result.name session.add(vote) registered = i18n.t('callback.vote.doodle_registered', locale=locale, vote_type=vote.type) context.query.answer(registered) return True
def handle_limited_vote(session, context, option): """Handle a limited vote.""" locale = option.poll.locale existing_vote = session.query(Vote) \ .filter(Vote.poll_option == option) \ .filter(Vote.user == context.user) \ .one_or_none() vote_count = session.query(Vote) \ .filter(Vote.poll == option.poll) \ .filter(Vote.user == context.user) \ .count() # Remove vote if existing_vote: session.delete(existing_vote) vote_removed = i18n.t('callback.vote.removed', locale=locale) respond_to_vote(session, vote_removed, context, option.poll, vote_count - 1, True) # Add vote elif existing_vote is None and vote_count < option.poll.number_of_votes: vote = Vote(context.user, option) session.add(vote) vote_registered = i18n.t('callback.vote.registered', locale=locale) respond_to_vote(session, vote_registered, context, option.poll, vote_count + 1, True) # Max votes reached else: no_left = i18n.t('callback.vote.no_left', locale=locale) respond_to_vote(session, no_left, context, option.poll) return False return True
def init_votes_for_new_options(session, poll: Poll, added_options: List[str]): """ When a new option is added, we need to create new votes for all users that have already voted for this poll. """ if not poll.is_priority(): return # Get all newly added options new_options = ( session.query(Option) .filter(Option.poll == poll) .filter(Option.name.in_(added_options)) .all() ) # The new options are already flushed. # Subtract the amount of new options to get the proper index. existing_options_count = len(poll.options) - len(new_options) users_that_voted = ( session.query(User).join(User.votes).filter(Vote.poll == poll).all() ) for user in users_that_voted: for index, option in enumerate(new_options): vote = Vote(user, option) vote.priority = existing_options_count + index user.votes.append(vote)
def init_votes_for_new_options(self, session): """ When a new option is added, we need to create new votes for all users that have already voted for this poll """ if not self.is_priority(): return from pollbot.models import User, Vote, PollOption users = session.query(User) \ .join(User.votes) \ .filter(Vote.poll == self) \ .all() new_options = session.query(PollOption) \ .filter(PollOption.poll == self) \ .outerjoin(Vote) \ .filter(Vote.id.is_(None)) \ .all() existing_options_count = len(self.options) - len(new_options) for user in users: for index, option in enumerate(new_options): vote = Vote(user, option) vote.priority = existing_options_count + index user.votes.append(vote)
def test_cascades_delete_vote(self, session, user, poll): option = PollOption(poll, 'option 0') session.add(option) vote = Vote(user, option) vote.priority = 0 session.add(vote) session.commit() session.delete(poll) session.commit() assert session.query(Vote).count() == 0
def test_cascades_dont_delete_poll(self, session, user, poll): option = PollOption(poll, 'option 0') session.add(option) vote = Vote(user, option) vote.priority = 0 session.add(vote) session.commit() session.delete(vote) session.commit() poll_exists = session.query( exists().where(Poll.id == poll.id)).scalar() assert poll_exists
def handle_limited_vote(session, context, option): """Handle a limited vote.""" locale = option.poll.locale existing_vote = ( session.query(Vote) .filter(Vote.option == option) .filter(Vote.user == context.user) .one_or_none() ) vote_count = ( session.query(Vote) .filter(Vote.poll == option.poll) .filter(Vote.user == context.user) .count() ) allowed_votes = option.poll.number_of_votes # Remove vote if existing_vote: session.delete(existing_vote) vote_removed = i18n.t("callback.vote.removed", locale=locale) remaining_votes = allowed_votes - (vote_count - 1) respond_to_vote( session, vote_removed, context, option.poll, remaining_votes, True ) # Add vote elif existing_vote is None and vote_count < allowed_votes: vote = Vote(context.user, option) session.add(vote) vote_registered = i18n.t("callback.vote.registered", locale=locale) remaining_votes = allowed_votes - (vote_count + 1) respond_to_vote( session, vote_registered, context, option.poll, remaining_votes, True ) # Max votes reached else: no_left = i18n.t("callback.vote.no_left", locale=locale) respond_to_vote(session, no_left, context, option.poll) return False return True
def handle_block_vote(session, context, option): """Handle a block vote.""" locale = option.poll.locale existing_vote = (session.query(Vote).filter(Vote.option == option).filter( Vote.user == context.user).one_or_none()) # Remove vote if existing_vote: session.delete(existing_vote) vote_removed = i18n.t("callback.vote.removed", locale=locale) respond_to_vote(session, vote_removed, context, option.poll) # Add vote elif existing_vote is None: vote = Vote(context.user, option) session.add(vote) vote_registered = i18n.t("callback.vote.registered", locale=locale) respond_to_vote(session, vote_registered, context, option.poll) return True
def init_votes(session, poll: Poll, user: User): """ Since Priority votes always need priorities, call this to create a vote for every option in the poll with a random priority for the given user. """ assert poll.is_priority() # Don't init votes, if there already is a vote any_vote = (session.query(Vote).filter(Vote.user == user).filter( Vote.poll == poll).first()) if any_vote is not None: return votes = [] for index, option in enumerate( random.sample(poll.options, len(poll.options))): vote = Vote(user, option) vote.priority = index votes.append(vote) session.add_all(votes)
def init_votes(self, session, user): """ Since Priority votes always need priorities, call this to create a vote for every option in the poll with a random priority for the given user """ assert self.is_priority() from pollbot.models import Vote votes_exist = (session.query(Vote).filter(Vote.user == user).filter( Vote.poll == self).first() is not None) if votes_exist: return votes = [] for index, option in enumerate( random.sample(self.options, len(self.options))): vote = Vote(user, option) vote.priority = index votes.append(vote) session.add_all(votes)
def handle_cumulative_vote(session, context, option, unlimited=False): """Handle a cumulative vote.""" locale = option.poll.locale existing_vote = session.query(Vote) \ .filter(Vote.poll_option == option) \ .filter(Vote.user == context.user) \ .one_or_none() vote_count = session.query(func.sum(Vote.vote_count)) \ .filter(Vote.poll == option.poll) \ .filter(Vote.user == context.user) \ .one() vote_count = vote_count[0] if vote_count is None: vote_count = 0 action = context.callback_result allowed_votes = 10000000 if not unlimited: allowed_votes = option.poll.number_of_votes # Upvote, but no votes left if not unlimited and action == CallbackResult.yes and vote_count >= allowed_votes: no_left = i18n.t('callback.vote.no_left', locale=locale) respond_to_vote(session, no_left, context, option.poll) return False # Early return if downvote on non existing vote if existing_vote is None and action == CallbackResult.no: respond_to_vote(session, 'Cannot downvote this option.', context, option.poll) return False if existing_vote: # Add to an existing vote if action == CallbackResult.yes: existing_vote.vote_count += 1 session.commit() remaining_votes = allowed_votes - (vote_count + 1) vote_registered = i18n.t('callback.vote.registered', locale=locale) respond_to_vote(session, vote_registered, context, option.poll, remaining_votes, not unlimited) # Remove from existing vote elif action == CallbackResult.no: existing_vote.vote_count -= 1 session.commit() remaining_votes = allowed_votes - (vote_count - 1) vote_removed = i18n.t('callback.vote.removed', locale=locale) respond_to_vote(session, vote_removed, context, option.poll, remaining_votes, not unlimited) # Delete vote if necessary if existing_vote.vote_count <= 0: session.delete(existing_vote) session.commit() # Add new vote elif existing_vote is None and action == CallbackResult.yes: vote = Vote(context.user, option) session.add(vote) session.commit() remaining_votes = allowed_votes - (vote_count + 1) vote_registered = i18n.t('callback.vote.registered', locale=locale) respond_to_vote(session, vote_registered, context, option.poll, remaining_votes, not unlimited) return True