Search code examples
python-3.xflasktweepytextblob

Having trouble passing arguments to decorated function in flask


I'm new to flask and python and am creating a project that pulls Twitter data to perform sentiment analyses on search terms using TextBlob to display some visual statistics. I've saved some of the stats as variables (percentages of positive, negative and neutral tweets) and am trying to pass them into a function to generate a pie chart. This function is decorated so that it will pass the resulting PNG file to an HTML page. I seem to be having issues passing these variables into my plotting function.

Here is part of my sentiment analyses:

def sentiment(userinput):
# creating object of TwitterClient Class
api = TwitterClient(userinput)
# calling function to get tweets
#searchterm = input("Enter query: ")
tweets = api.get_tweets(query=api.searchterm, count=10)

# picking positive tweets from tweets
ptweets = [tweet for tweet in tweets if tweet['sentiment'] == 'positive']
# percentage of positive tweets
ptweet_analyses_pie = 100 * len(ptweets) / len(tweets)

# picking negative tweets from tweets
ntweets = [tweet for tweet in tweets if tweet['sentiment'] == 'negative']
# percentage of negative tweets
ntweets_analyses_pie = (100 * len(ntweets) / len(tweets))

# percentage of neutral tweets
#leftoverTweets = tweets - ntweets - ptweets
nut_tweet_analyses_pie = (100 * (len(tweets) - len(ntweets) - len(ptweets)) / len(tweets))

pie_chart_img = pie_chart(ptweet_analyses_pie, nut_tweet_analyses_pie, ntweets_analyses_pie)

return ptweets, ntweets, ptweet_analyses, ntweets_analyses, nut_tweet_analyses, pie_chart_img

Here is where the sentiment() is being called:

@app.route('/render_Data', methods = ['GET', 'POST'])
def render_Data():
    if request.method == 'POST':
        tweets=request.form['tweets']
        ptweets, ntweets, ptweet_analyses, ntweets_analyses, nut_tweet_analyses, pie_chart_img = sentiment(tweets)

    return render_template('render_Data.html', ptweets = ptweets, ntweets = ntweets, ptweet_analyses = ptweet_analyses,
ntweets_analyses = ntweets_analyses, nut_tweet_analyses = nut_tweet_analyses, pie_chart_img = pie_chart_img)

Here is where I make the pie_chart.png and give it an url to be rendered as html without saving to static.

@app.route('/pie_chart.png')
def pie_chart(x,y,z):
    labels = 'Positive', 'Negative', 'Neutral'
    sizes = [x,y,z]
    colors = ['gold', 'pink', 'lightskyblue']
    explode = (0, 0, 0)  # explode 1st slice
    # Plot
    plt.axis('equal')
    plt.pie(sizes, explode=explode, labels=labels, colors=colors,
        autopct='%1.1f%%', shadow=True, startangle=140)
    img = BytesIO()
    plt.savefig(img)
    response = make_response(img.getvalue())
    response.mimetype = 'image/png'
    return response

And finally the HTML:

<img src='{{url_for('pie_chart')}}'>

Unfortunately I get an error when I try to render the results: pie_chart() missing 3 required positional arguments: 'x', 'y', and 'z'

Thanks in advance! I know my code is a little elementary as I'm still in the fledging phases of coding, so please go easy in your response!


Solution

  • You are really close to the right solution! The route above the definition should also contain the arguments needed for the function: @app.route('/piechart.png/<x>/<y>/<z>'), and you should also specify these arguments when calling the url: <img src='{{url_for('pie_chart', x=1, y=2, z=3)}}'>

    from flask import Flask, render_template_string, url_for, make_response
    from io import BytesIO
    import matplotlib
    matplotlib.use('Agg')
    from matplotlib import pyplot as plt
    
    app = Flask(__name__)
    
    @app.route('/piechart.png/<x>/<y>/<z>', methods=['GET', 'POST'])
    def chart_maker(x, y, z):
        labels = 'Positive', 'Negative', 'Neutral'
        sizes = [x, y, z]
        colors = ['gold', 'pink', 'lightskyblue']
        explode = (0, 0, 0)
        plt.axis('equal')
        plt.pie(sizes, explode=explode, labels=labels, colors=colors,
                autopct='%1.1f%%', shadow=True, startangle=140)
        img = BytesIO()
        plt.savefig(img)
        response = make_response(img.getvalue())
        response.mimetype = 'image/png'
        return response
    
    @app.route('/display_chart')
    def display_chart():
        return render_template_string('<img src="{{url_for("chart_maker", x=1, y=2, z=3)}}">')
    
    app.run() 
    

    when visiting http://127.0.0.1:5000/display_chart returns: this image: