def _format_running_poll(poll):
    fields = [{
        'short': False,
        'value': tr("*Number of voters: {}*").format(poll.num_voters()),
        'title': ""
    }]
    if poll.public:
        fields += [{
            'short':
            False,
            'value':
            tr(":warning: *This poll is public. When it closes the"
               " participants and their answers will be visible.*"),
            'title':
            ""
        }]
    if poll.max_votes > 1:
        fields += [{
            'short': False,
            'value': tr("*You have {} votes*").format(poll.max_votes),
            'title': ""
        }]

    return {
        'response_type':
        'in_channel',
        'attachments': [{
            'text': poll.message,
            'actions': format_actions(poll),
            'fields': fields
        }]
    }
Beispiel #2
0
def end_poll():
    """Ends the poll.
    Called by the 'End Poll' actions.
    Only the user that created the poll is allowed to end it.
    All other user will receive an ephemeral error message.
    """
    json = request.get_json()
    user_id = json['user_id']
    team_id = json['team_id']
    poll_id = json['context']['poll_id']
    request.user_id = user_id

    try:
        poll = Poll.load(poll_id)
    except InvalidPollError:
        return jsonify({
            'ephemeral_text':
            tr("This poll is not valid anymore.\n"
               "Sorry for the inconvenience.")
        })

    app.logger.info('Ending poll "%s"', poll_id)

    # only the creator and admins may end a poll
    can_end_poll = \
        user_id == poll.creator_id or \
        is_admin_user(user_id) or \
        is_team_admin(user_id=user_id, team_id=team_id)

    if can_end_poll:
        poll.end()
        return jsonify({'update': {'props': format_poll(poll)}})

    return jsonify(
        {'ephemeral_text': tr("You are not allowed to end this poll")})
def _format_finished_poll(poll):
    votes = [(vote, vote_id) for vote_id, vote in enumerate(poll.vote_options)]

    if poll.bars:
        # bars should be displayed from long to short
        votes.sort(key=lambda v: poll.count_votes(v[1]), reverse=True)

    return {
        'response_type':
        'in_channel',
        'attachments': [{
            'text':
            poll.message,
            'fields':
            [{
                'short': False,
                'value': tr("*Number of voters: {}*").format(
                    poll.num_voters()),
                'title': ""
            }] + [{
                'short': not poll.bars,
                'title': vote,
                'value': _format_vote_end_text(poll, vote_id)
            } for vote, vote_id in votes]
        }]
    }
def format_actions(poll):
    """Returns the JSON data of all available actions of the given poll.
    Additional to the options of the poll, a 'End Poll' action
    is appended.
    The returned list looks similar to this:
    ```
    [{
        "name": "<First Option> (0)",
        "integration": {
            "context": {
                "poll_id": "<unique_id>",
                "vote": 0
            },
            "url": "http://<hostname:port>/vote"
        }
    },
    ... additional entries for all poll options ...
    {
        "name": "End Poll",
        "integration": {
            "url": "http://<hostname:port>/end",
            "context": {
                "poll_id": "<unique_id>"
            }
        }
    }]
    ```
    """
    with force_locale(poll.locale):
        options = poll.vote_options
        name = "{name}"
        if not poll.secret:
            # display current number of votes
            name += " ({votes})"
        actions = [{
            'name':
            name.format(name=vote, votes=poll.count_votes(vote_id)),
            'integration': {
                'url': url_for('vote', _external=True),
                'context': {
                    'vote': vote_id,
                    'poll_id': poll.id
                }
            }
        } for vote_id, vote in enumerate(options)]
        # add action to end the poll
        actions.append({
            'name': tr("End Poll"),
            'integration': {
                'url': url_for('end_poll', _external=True),
                'context': {
                    'poll_id': poll.id
                }
            }
        })
        return actions
Beispiel #5
0
def _format_running_poll(poll):
    fields = [{
        'short': False,
        'value': tr("*Number of voters: {}*").format(poll.num_voters()),
        'title': ""
    }]
    if poll.public:
        fields += [{
            'short':
            False,
            'value':
            tr(":warning: *This poll is public. When it closes the"
               " participants and their answers will be visible.*"),
            'title':
            ""
        }]
    if poll.max_votes > 1:
        fields += [{
            'short': False,
            'value': tr("*You have {} votes*").format(poll.max_votes),
            'title': ""
        }]
    if not poll.secret and poll.bars:
        votes = [(vote, vote_id)
                 for vote_id, vote in enumerate(poll.vote_options)
                 if poll.count_votes(vote_id) > 0]

        fields += [{
            'short': False,
            'title': vote,
            'value': _format_vote_end_text(poll, vote_id)
        } for vote, vote_id in votes]

    return {
        'response_type':
        'in_channel',
        'attachments': [{
            'text': poll.message,
            'actions': format_actions(poll),
            'fields': fields
        }]
    }
Beispiel #6
0
def vote():
    """Places a vote for a user.
    Called through the URL in the corresponding action (see
    formatters.format_actions).
    The JSON `context` is expected to contain a valid poll_id and the
    vote_id to vote for.
    """
    json = request.get_json()
    user_id = json['user_id']
    poll_id = json['context']['poll_id']
    vote_id = json['context']['vote']
    request.user_id = user_id

    try:
        poll = Poll.load(poll_id)
    except InvalidPollError:
        return jsonify({
            'ephemeral_text':
            tr("This poll is not valid anymore.\n"
               "Sorry for the inconvenience.")
        })

    app.logger.info('Voting in poll "%s" for user "%s": %i', poll_id, user_id,
                    vote_id)
    try:
        poll.vote(user_id, vote_id)
    except NoMoreVotesError:
        return jsonify({
            'ephemeral_text':
            tr("You already used all your votes.\n"
               "Click on a vote to unselect it again.")
        })

    return jsonify({
        'update': {
            'props': format_poll(poll)
        },
        'ephemeral_text':
        tr("Your vote has been updated:\n{}").format(
            format_user_vote(poll, user_id))
    })
Beispiel #7
0
def _format_finished_poll(poll):
    votes = [(vote, vote_id) for vote_id, vote in enumerate(poll.vote_options)]

    return {
        'response_type':
        'in_channel',
        'attachments': [{
            'text':
            poll.message,
            'fields':
            [{
                'short': False,
                'value': tr("*Number of voters: {}*").format(
                    poll.num_voters()),
                'title': ""
            }] + [{
                'short': not poll.bars,
                'title': vote,
                'value': _format_vote_end_text(poll, vote_id)
            } for vote, vote_id in votes]
        }]
    }
def format_help(command, locale='en'):
    """Returns a help string describing the poll slash command."""
    help_lines = []

    help_file = os.path.join(os.path.dirname(__file__), 'translations', locale,
                             'help.md')
    try:
        with open(help_file) as f:
            help_lines = f.readlines()
    except FileNotFoundError:
        if locale != 'en':
            return format_help(command, 'en')

    if not help_lines:
        return tr("Help file not found.")  # pragma: no cover

    # remove the description of all superfluous options
    help_text = ''.join(
        [line for line in help_lines if not _is_superfluous(line)])

    # replace placeholder for slash command
    help_text = help_text.format(command=command)

    return help_text
Beispiel #9
0
def poll():
    """Creates a new poll.
    Directly called by Mattermost.
    Example response data:
    ```
    {
        "response_type": "in_channel",
        "attachments": [{
            "text": "<Poll message>",
            "actions": [
            {
                "name": "<First Option> (0)",
                "integration": {
                    "context": {
                        "poll_id": "<unique_id>",
                        "vote": 0
                    },
                    "url": "http://<hostname:port>/vote"
                }
            },
            ... additional entries for all poll options ...
            {
                "name": "End Poll",
                "integration": {
                    "url": "http://<hostname:port>/end",
                    "context": {
                        "poll_id": "<unique_id>"
                    }
                }
            }],
            "fields": [
            {
                "short": false,
                "title": "",
                "value": "Number of Votes: 1"
            }]
        }]
    }
    ```
    """
    if hasattr(settings, 'MATTERMOST_TOKEN'):
        warnings.warn("MATTERMOST_TOKEN is deprecated, use MATTERMOST_TOKENS \
                      instead",
                      category=DeprecationWarning)
        settings.MATTERMOST_TOKENS = [settings.MATTERMOST_TOKEN]
        settings.MATTERMOST_TOKEN = None

    if settings.MATTERMOST_TOKENS:
        token = request.form['token']
        if token not in settings.MATTERMOST_TOKENS:
            return jsonify({
                'response_type':
                'ephemeral',
                'text':
                tr("The integration is not correctly set up: "
                   "Invalid token.")
            })

    if 'user_id' not in request.form:
        abort(400)
    if 'text' not in request.form:
        abort(400)
    user_id = request.form['user_id']
    request.user_id = user_id

    app.logger.debug('Received command: %s', request.form['text'])

    # the poll should have a fixed locale, otherwise it
    # changes for everyone every time someone votes
    locale = flask_babel.get_locale().language

    if request.form['text'].strip() == 'help':
        return jsonify({
            'response_type': 'ephemeral',
            'text': format_help(request.form['command'], locale)
        })

    args = parse_slash_command(request.form['text'])
    if not args.message:
        text = tr("**Please provide a message.**\n\n"
                  "**Usage:**\n{help}").format(
                      help=format_help(request.form['command'], locale))
        return jsonify({'response_type': 'ephemeral', 'text': text})
    if args.public:
        if not settings.MATTERMOST_URL or not settings.MATTERMOST_PA_TOKEN:
            return jsonify({
                'response_type':
                'ephemeral',
                'text':
                tr("Public polls are not available with the "
                   "current setup. Please check with you "
                   "system administrator.")
            })

    if args.locale:
        locale = args.locale

    poll = Poll.create(user_id,
                       message=args.message,
                       locale=locale,
                       vote_options=args.vote_options,
                       secret=not args.progress,
                       public=args.public,
                       max_votes=args.max_votes,
                       bars=args.bars)

    app.logger.info('Creating Poll: %s', poll.id)

    return jsonify(format_poll(poll))
Beispiel #10
0
def status():
    """Returns a simple message if the server is running."""
    return tr("Poll server is running")
Beispiel #11
0
def poll():
    """Creates a new poll.
    Directly called by Mattermost.
    Example response data:
    ```
    {
        "response_type": "in_channel",
        "attachments": [{
            "text": "<Poll message>",
            "actions": [
            {
                "name": "<First Option> (0)",
                "integration": {
                    "context": {
                        "poll_id": "<unique_id>",
                        "vote": 0
                    },
                    "url": "http://<hostname:port>/vote"
                }
            },
            ... additional entries for all poll options ...
            {
                "name": "End Poll",
                "integration": {
                    "url": "http://<hostname:port>/end",
                    "context": {
                        "poll_id": "<unique_id>"
                    }
                }
            }],
            "fields": [
            {
                "short": false,
                "title": "",
                "value": "Number of Votes: 1"
            }]
        }]
    }
    ```
    """
    if hasattr(settings, 'MATTERMOST_TOKEN'):
        warnings.warn("MATTERMOST_TOKEN is deprecated, use MATTERMOST_TOKENS \
                      instead",
                      category=DeprecationWarning)
        settings.MATTERMOST_TOKENS = [settings.MATTERMOST_TOKEN]
        settings.MATTERMOST_TOKEN = None

    if settings.MATTERMOST_TOKENS:
        token = request.form['token']
        if token not in settings.MATTERMOST_TOKENS:
            return jsonify({
                'response_type':
                'ephemeral',
                'text':
                tr("The integration is not correctly set up: "
                   "Invalid token.")
            })

    if 'user_id' not in request.form:
        abort(400)
    if 'text' not in request.form:
        abort(400)
    user_id = request.form['user_id']
    request.user_id = user_id

    app.logger.debug('Received command: %s', request.form['text'])

    # the poll should have a fixed locale, otherwise it
    # changes for everyone every time someone votes
    locale = flask_babel.get_locale().language

    if request.form['text'].strip() == 'help':
        return jsonify({
            'response_type': 'ephemeral',
            'text': format_help(request.form['command'], locale)
        })

    args = parse_slash_command(request.form['text'])

    #debug
    #return jsonify({'response_type': 'ephemeral', 'text': str(args.progress)+str(args.lunch)+str(args.message)})

    # lunch
    # built new message and vote_options
    if args.lunch:
        lunch = Lunch()
        restaurants = lunch.read_restaurant()
        try:
            num_restaurant = int(args.message)
        except:
            num_restaurant = len(
                restaurants
            )  # if no number is provider as a message, just list all restautrant in the db
            #return jsonify({'ephemeral_text':tr("Please provide a number.")})
        restaurants_subset = random.sample(restaurants, num_restaurant)
        new_vote_options = restaurants_subset

        args_dict = args._asdict()
        args_dict['message'] = "Let's vote for lunch!"
        args_dict['vote_options'] = new_vote_options

        # copy from parse_slash_command
        Arguments = namedtuple('Arguments', [
            'message', 'vote_options', 'progress', 'public', 'max_votes',
            'bars', 'locale', 'lunch', 'lunchadd', 'lunchrm', 'lunchls'
        ])

        args = Arguments(**args_dict)

    if args.lunchadd:
        lunch = Lunch()
        flag = lunch.add_restaurant(author_id=user_id, restaurant=args.message)
        if flag:
            return jsonify({
                'response_type': 'ephemeral',
                'text': tr("Successfully added restaurant.")
            })
        else:
            return jsonify({
                'response_type': 'ephemeral',
                'text': tr("Error in adding restaurant.")
            })
    if args.lunchrm:
        lunch = Lunch()
        flag = lunch.rm_restaurant(args.message)
        if flag:
            return jsonify({
                'response_type': 'ephemeral',
                'text': tr("Successfully removed restaurant.")
            })
        else:
            return jsonify({
                'response_type': 'ephemeral',
                'text': tr("Error in removing restaurant.")
            })
    if args.lunchls:
        lunch = Lunch()
        restaurants = lunch.read_restaurant()
        restaurants_str = ",".join(restaurants)
        return jsonify({'response_type': 'ephemeral', 'text': restaurants_str})

    if not args.message:
        text = tr("**Please provide a message.**\n\n"
                  "**Usage:**\n{help}").format(
                      help=format_help(request.form['command'], locale))
        return jsonify({'response_type': 'ephemeral', 'text': text})
    if args.public:
        if not settings.MATTERMOST_URL or not settings.MATTERMOST_PA_TOKEN:
            return jsonify({
                'response_type':
                'ephemeral',
                'text':
                tr("Public polls are not available with the "
                   "current setup. Please check with you "
                   "system administrator.")
            })

    if args.locale:
        locale = args.locale

    poll = Poll.create(user_id,
                       message=args.message,
                       locale=locale,
                       vote_options=args.vote_options,
                       secret=not args.progress,
                       public=args.public,
                       max_votes=args.max_votes,
                       bars=args.bars)

    app.logger.info('Creating Poll: %s', poll.id)

    return jsonify(format_poll(poll))