def _process_muse(muse): """ Processes a Muse's tweets, creating Tweet objects and saving them to the db. """ username = muse.username logger.info("Collecting tweets for %s..." % username) try: tweets = twitter.tweets(username=username) except TweepError: return [] new_tweets = [] for tweet in tweets: data = {"body": tweet["body"], "tid": tweet["tid"], "username": username} t = Tweet(**data) try: t.save() new_tweets.append(tweet) except (NotUniqueError, DuplicateKeyError, OperationError): # Duplicate tweet pass return new_tweets
def api_tweet(): try: data = json.loads( request.data, object_hook=lambda x: defaultdict(lambda: None, x) ) except JSONDecodeError: return error_response(["Invalid request."]) tweet_text = data["text"] or "" tweet_text = tweet_text.strip().replace("\r\n", "\n") tweet_len = len(tweet_text) if tweet_len < 1: return error_response(["Your tweet must be at least one character."]) if tweet_len > 120: return error_response(["Your tweet must be at most 120 characters."]) image_id = data["imageId"] if image_id is not None: image_exists = Image.query.filter_by(id=image_id).count() > 0 if not image_exists: return error_response(["Image not found"]) tweet = Tweet( text=tweet_text, poster_id=int(current_user.get_id()), image_id=image_id ) db.session.add(tweet) db.session.commit() return success_response(tweet.to_dict())
def test_tweet_delete_apikey_KO(self): first_tweet = Tweet("First tweet") first_tweet.user = self.user db.session.add(first_tweet) db.session.commit() response = self.client.delete("/tweets/1") self.assertIn("401", response.status)
def sample_tweets(client): tweet1 = Tweet(party="GOP", person="Trump", tweet="Bigly") db.session.add(tweet1) tweet2 = Tweet(party="GOP", person="Trump", tweet="Covfefe") db.session.add(tweet2) db.session.commit() return [tweet1, tweet2]
def test_api_can_retweet_a_status(self): '''Test API can retweet a Status''' status_to_retweet = {'text': "Hahaha retweet this i dare you"} user = { 'username': "******", 'email': "*****@*****.**", 'password': "******" } with self.app.app_context(): u = User(email=user['email'], password_hash=user['password'], username=user['username'], verified=True) u.insert() s = Tweet(text=status_to_retweet['text'], user=u) s.insert() rv = self.client().post( '/statuses/retweet', data=json.dumps({'id': s.id}), content_type='application/json', headers={'x-access-token': self.auth_token}) result_in_json = json.loads( rv.data.decode('utf-8').replace("'", "\"")) self.assertEqual(rv.status_code, 201) self.assertIn('success', str(result_in_json['data'])) self.client().post( '/statuses/reply', data=json.dumps({'id': s.id, 'text': 'Hahaha now am gonna also reply'}), content_type='application/json', headers={'x-access-token': self.auth_token})
def _process_muse(muse): """ Processes a Muse's tweets, creating Tweet objects and saving them to the db. """ username = muse.username logger.info('Collecting tweets for %s...' % username) try: tweets = twitter.tweets(username=username) except TweepError: return [] new_tweets = [] for tweet in tweets: data = { 'body': tweet['body'], 'tid': tweet['tid'], 'username': username } t = Tweet(**data) try: t.save() new_tweets.append(tweet) except (NotUniqueError, DuplicateKeyError, OperationError): # Duplicate tweet pass return new_tweets
def test_tweet_delete(self): first_tweet = Tweet() first_tweet.text = "First tweet" db.session.add(first_tweet) db.session.commit() self.client.delete("/tweets/1") self.assertIsNone(db.session.query(Tweet).get(1))
def new_tweet(request): if request.method == 'POST': tweet = Tweet(tweet = request.POST['tweet'], user=request.user) tweet.save() messages.success(request, 'Twiteado') return redirect(reverse('home')) return render(request, 'new_tweet.html')
def test_auto_increment_after_adding_a_tweet(self): tweet_repository = TweetRepository() tweet = Tweet("RAPH") tweet2 = Tweet("JOHN") tweet_repository.add(tweet) tweet_repository.add(tweet2) self.assertEqual(tweet.id, 1) self.assertEqual(tweet2.id, 2)
def test_increment_id(self): repository = Repository() tweet1 = Tweet("Ah que coucou.") repository.add(tweet1) self.assertEqual(tweet1.id, 1) tweet2 = Tweet("Tirelipimpon sur le Chihuahua.") repository.add(tweet2) self.assertEqual(tweet2.id, 2)
def setUp(self): db.create_all() first_tweet = Tweet(text="First tweet") db.session.add(first_tweet) db.session.commit() second_tweet = Tweet(text="Secound tweet") db.session.add(second_tweet) db.session.commit()
def test_auto_increment_of_ids(self): repository = TweetRepository() first_tweet = Tweet("a first tweet") repository.add(first_tweet) self.assertEqual(first_tweet.id, 1) second_tweet = Tweet("a second tweet") repository.add(second_tweet) self.assertEqual(second_tweet.id, 2)
def test_tweets_index_non_empty(self): first_tweet = Tweet(text="First tweet") second_tweet = Tweet(text="Second tweet") db.session.add_all([first_tweet, second_tweet]) db.session.commit() response = self.client.get("/tweets") response_tweets = response.json self.assertEqual(len(response_tweets), 2)
def post(self): """ Create a new tweet """ tweet_to_add = Tweet() tweet_to_add.text = api.payload['text'] db.session.add(tweet_to_add) db.session.commit() return tweet_to_add, 201
def reply_status(current_user): data = request.get_json() s = Tweet.query.filter_by(id=data['id']).first() if not s: return jsonify({'data': 'Resource not found', 'error': None}), 404 reply = Tweet(text=data['text'], user=current_user, in_reply_to_status=s) reply.insert() db.session.commit() return jsonify({'data': 'success', 'error': None}), 201
def tweet_create(): data = request.json or request.form if 'party' not in data or 'person' not in data or 'tweet' not in data: return bad_request('must include party & person & tweet') tweet = Tweet(party=data.get("party"), person=data.get("person"), tweet=data.get("tweet")) db.session.add(tweet) db.session.commit() return jsonify(tweet.to_dict())
def post(self): text = api.payload["text"] if len(text) > 0: tweet = Tweet() tweet.text = text db.session.add(tweet) db.session.commit() return tweet, 201 else: return abort(422, "Tweet text can't be empty")
def test_tweet_update(self): first_tweet = Tweet() first_tweet.text = "First tweet" db.session.add(first_tweet) db.session.commit() response = self.client.patch("/tweets/1", json={'text': 'New text'}) updated_tweet = response.json self.assertEqual(response.status_code, 200) self.assertEqual(updated_tweet["id"], 1) self.assertEqual(updated_tweet["text"], "New text")
def test_tweet_show(self): first_tweet = Tweet() first_tweet.text = "First tweet" db.session.add(first_tweet) response = self.client.get("/tweets/1") response_tweet = response.json print(response_tweet) self.assertEqual(response_tweet["id"], 1) self.assertEqual(response_tweet["text"], "First tweet") self.assertIsNotNone(response_tweet["created_at"])
def test_tweet_patch_invalid(self): first_tweet = Tweet("First tweet") first_tweet.user = self.user db.session.add(first_tweet) db.session.commit() json_data = json.dumps({"textdata": "Patched via API call"}) response = self.client.patch("/tweets/1", data=json_data, content_type='application/json') self.assertIn("400", response.status)
def test_tweet_show_all(self): first_tweet = Tweet(text="First tweet") db.session.add(first_tweet) second_tweet = Tweet(text="First tweet") db.session.add(second_tweet) db.session.commit() response = self.client.get("/tweets") response_tweet = response.json print(response_tweet) self.assertEqual(len(response_tweet), 2)
def share_a_status(current_user): data = request.get_json() s = Tweet.query.filter_by(id=data['id']).first() if not s: return jsonify({'data': 'Resource not found', 'error': None}), 404 retweet = Tweet(text=data.get("text", None), user=current_user, retweet_status=s) retweet.insert() db.session.commit() return jsonify({'data': 'success', 'error': None}), 201
def test_tweet_delete(self): first_tweet = Tweet("First tweet") first_tweet.user = self.user db.session.add(first_tweet) db.session.commit() response = self.client.delete( "/tweets/1", headers={"authorization": "TWITTER-APIKEY MYAPIKEY"}) self.assertIn("204", response.status) response = self.client.delete( "/tweets/1", headers={"authorization": "TWITTER-APIKEY MYAPIKEY"}) self.assertIn("404", response.status)
def test_all_tweets(self): first_tweet = Tweet("First tweet") second_tweet = Tweet("Second tweet") db.session.add(first_tweet) db.session.add(second_tweet) db.session.commit() response = self.client.get("/tweets") self.assertIn("200", response.status) tweets = response.json self.assertEqual(len(tweets), 2) self.assertEqual(tweets[0]["text"], "First tweet") self.assertEqual(tweets[1]["text"], "Second tweet")
def test_tweet_patch_invalid_apikey_OK(self): first_tweet = Tweet("First tweet") first_tweet.user = self.user db.session.add(first_tweet) db.session.commit() json_data = json.dumps({"textdata": "Patched via API call"}) response = self.client.patch( "/tweets/1", headers={"authorization": "TWITTER-APIKEY MYAPIKEY"}, data=json_data, content_type='application/json') self.assertIn("400", response.status)
def seed_tweets(self): '''Fetches 1 tweet (their most recent) per tracked user IF the user doesn't have any tweets in the DB already. returns: None ''' for user in self.tracked_users: if not user.tweets: tweet = fetch_most_recent_tweet(user.id) if tweet is not None: Tweet.create(tweet, user, needs_to_be_sent=False)
def post(self): text = api.payload["text"] user = api.payload["user"] if len(text) > 0: tweet = Tweet() tweet.text = text user = db.session.query(User).filter_by(name=user).first() tweet.user_id = user.id db.session.add(tweet) db.session.commit() return tweet, 201 else: return abort(422, "Tweet text can't be empty")
def post(self): try: text = api.payload["text"] except: return None, 400 user = get_api_key_user() if user == None: return "Wrong api key or no api key provided", 401 tweet = Tweet(text) tweet.user = user db.session.add(tweet) db.session.commit() return tweet, 201
def test_get_all_tweets(self): user = User(username='******') first_tweet = Tweet(text="First tweet", user=user) second_tweet = Tweet(text="Second tweet", user=user) db.session.add(first_tweet) db.session.add(second_tweet) db.session.commit() response = self.client.get("/tweets") tweets = response.json self.assertEqual(type(tweets), list) self.assertEqual(tweets[0]['text'], "First tweet") self.assertEqual(tweets[0]['user']['username'], "ssaunier") self.assertEqual(tweets[1]['text'], "Second tweet") self.assertEqual(tweets[1]['user']['username'], "ssaunier")
def save_tweets(tweets, search_term, location_search_term=None): """ Saves given tweet data in MongoDB database. :param tweets: Tweet data :param search_term: Search term entered by user :param location_search_term: Location search term entered by user (optional) """ try: if location_search_term: # Cycle through list of Tweets for status in tweets: tweet_id = status['tweet_id'] # Check if tweet already exists in db, skip this iteration if it does id_query = len(Tweet.objects(tweet_id=tweet_id)) if id_query >= 1: continue # Define record to save to db record = Tweet( tweet_id=status['tweet_id'], tweet_time=status['created_at'], tweet_text=status['text'], tweet_user=status['user'], tweet_user_fullname=status['user_fullname'], profile_image_url=status['profile_image_url'], sentiment_type=status['sentiment'], sentiment_score=status['sentimentScore'], location_geo=status['location_geo'], location_address=status['location_address'], keyword_search_term=search_term, location_search_term=location_search_term ) # Save to DB record.save() else: # Cycle through list of processed Tweets for status in tweets: tweet_id = status['tweet_id'] # Check if tweet already exists in db, skip this iteration if it does id_query = len(Tweet.objects(tweet_id=tweet_id)) if id_query >= 1: continue # Define record to save to db record = Tweet( tweet_id=status['tweet_id'], tweet_time=status['created_at'], tweet_text=status['text'], tweet_user=status['user'], tweet_user_fullname=status['user_fullname'], profile_image_url=status['profile_image_url'], sentiment_type=status['sentiment'], sentiment_score=status['sentimentScore'], keyword_search_term=search_term ) # Save to DB record.save() except Exception: raise Exception("Database error")
def on_data(self, data): all_data = json.loads(data) tweet = all_data["text"] username = all_data["user"]["screen_name"] language = all_data["lang"] # en # nl if 'nl' in language: if 'RT' not in tweet: t = Tweet(tweet_text=tweet, twitter_user=username, keyword=keyword, discovery_date=timezone.now()) t.save() print username + ': ' + tweet print " " return True
def db_import(filename): print(filename) basedir = os.path.abspath(os.path.dirname(__file__)) import_file = os.path.join(basedir, 'static/import/' + filename + '.csv') log_file = os.path.join(basedir, 'log/err_' + filename + '.log') tID_column = 1 given_text_column = 2 #print("Pre: ", Tweet.query.all()) with open(import_file) as csv_file: csv_reader = csv.reader(csv_file, delimiter=',') for row in csv_reader: #Nonunique tID handler if Tweet.query.get(int(row[tID_column])): print("<Tweet " + row[tID_column] + "> was found in the database already" + time.time(), file=open(log_file, "a")) new_tweet = Tweet( tID=int(row[tID_column]), given_text=row[given_text_column], selected_text="", primary_sent=-1, secondary_sent=-1, ) print(new_tweet) db.session.add(new_tweet) db.session.commit() #print("Post: ", Tweet.query.all()) return redirect('/')
def post(self): new_value = message_parser.parse_args()['message'] result = tweet_repository.add(Tweet(new_value)) if result is None: api.abort(404) else: return result, 201
def get_recent_tweets(self): '''Fetches all recent tweets for all the tracked users and then inserts them into the DB. returns: None ''' for user in self.tracked_users: last_saved_tweet = Tweet.get_most_recent_by_user_id(user.id) # This is for certain users who have their twitter empty (EG: Mana Aloe). if last_saved_tweet is not None: new_tweets = fetch_new_tweets(user.id, last_saved_tweet.id) for tweet in new_tweets: if not Tweet.exists_by_id(tweet['id']): Tweet.create(tweet, user)
def search_keyword(keyword, count, location=None): """ Performs a Twitter search for a given keyword. Will restrict keyword search to within 10 miles radius of given location (if provided). :param keyword: Keyword to search :param location: Location object from geopy to restrict results to (optional) :param count: Number of Tweets to search for :return: List of dictionaries containing parsed Tweet data from search """ # Perform OAuth connection to Twitter, creates instance of Twython twitter = twitter_auth() is_location_search = False in_db = False # List to store our data tweets = [] # Attempt to query Twitter REST API using Twython try: if location is None: search_result = twitter.search(q=keyword, lang="en", count=count) else: is_location_search = True search_result = twitter.search(q=keyword, lang="en", geocode="%s,%s,10mi" % (location.latitude, location.longitude), count=count) except Exception: raise Exception("No Twitter results returned") # Cycle through results and parse the data we want, save as dictionary and store in 'tweets' list for status in search_result['statuses']: tweet_id = status['id'] # Check if Tweet is already in DB id_query = len(Tweet.objects(tweet_id=tweet_id)) if id_query >= 1: in_db = True continue tweet = {} tweet['tweet_id'] = tweet_id tweet['text'] = status['text'].strip() tm = status['created_at'] tm = datetime.strptime(tm, '%a %b %d %H:%M:%S +0000 %Y') tweet['created_at'] = tm tweet['user'] = status['user']['screen_name'].strip() tweet['user_fullname'] = status['user']['name'] tweet['profile_image_url'] = status['user']['profile_image_url'] if is_location_search: tweet['location_geo'] = {"latitude": location.latitude, "longitude": location.longitude} tweet['location_address'] = location.address # Append parsed tweet data dictionary to results list tweets.append(tweet) # If no search results returned and data is not in our database, raise exception if not tweets and not in_db: raise Exception("No Twitter results returned") return tweets
def done(request): """Login complete view, displays user data""" user = get_user(request.user) if not user.configured: return HttpResponseRedirect('/config/') # Redirect after POST new_posttweet = False if request.method == 'POST': # If the form has been submitted... tweetf = TweetForm(request.POST) # A form bound to the POST data if tweetf.is_valid(): # All validation rules pass #Save the tweet in the table text = tweetf.cleaned_data['text'] pub_date = datetime.utcnow() t = Tweet(text=text, pub_date=pub_date, user=user) t.save() new_posttweet = user.show_modal_new_tweet() if user.alwaysupdate: tweet = _("I saved a tweet that will be published when I die with http://foowill.com @foo_will") try: user.update_twitter_status(tweet) except TweepError: count = Tweet.objects.filter(user=user).count() user.update_twitter_status("%s (%d)" % (tweet, count)) except: pass user.posts = user.posts + 1 user.save() else: tweetf = TweetForm() tweets = Tweet.objects.filter(user=user).order_by('-pub_date') updatetweetform = UpdateTweetForm() ctx = { 'tweetform': tweetf, 'tweets': tweets, 'user': user, 'updatetweetform': updatetweetform, 'new_posttweet': new_posttweet, } return render_to_response('done.html', ctx, RequestContext(request))
def delete(self, username): context = self.get_context(username) muse = context.get('muse') # Fetch and clear out this user's tweets. tweets = Tweet.objects(username=username) for tweet in tweets: tweet.delete() muse.delete() return jsonify({'success':True})
def search_user(screen_name, count): """ Performs a Twitter search for a given username. :param screen_name: Username of Twitter user to search for :param count: Number of the user's Tweets to return :return: List of dictionaries containing parsed Tweet data from search """ # Perform OAuth connection to Twitter, creates instance of Twython twitter = twitter_auth() in_db = False # List to store our results tweets = [] # Attempt to query Twitter REST API using Twython try: search_result = twitter.get_user_timeline(screen_name=screen_name, count=count) except Exception: raise Exception("No Twitter results returned") if search_result[0]['user']['lang'] != "en": # If not English, we can't perform sentiment analysis (limitation of AlchemyAPI) raise Exception("Not an English language user") # Cycle through results and parse the data we want, save as dictionary and store in 'tweets' list for status in search_result: tweet_id = status['id'] # Check if Tweet is already in DB id_query = len(Tweet.objects(tweet_id=tweet_id)) if id_query >= 1: in_db = True continue tweet = {} tweet['tweet_id'] = tweet_id tweet['text'] = status['text'].strip() tm = status['created_at'] tm = datetime.strptime(tm, '%a %b %d %H:%M:%S +0000 %Y') tweet['created_at'] = tm tweet['user'] = screen_name[1:] tweet['user_fullname'] = status['user']['name'] tweet['profile_image_url'] = status['user']['profile_image_url'] # Add parsed tweet data dictionary to results list tweets.append(tweet) # If no search results returned and data is not in our database, raise exception if not tweets and not in_db: raise Exception("No Twitter results returned") return tweets
def vote(self, tweet_id, count): """Process a vote 'up' for given user_name on given tweet""" tweet_info = self.get_tweet_info(tweet_id) if tweet_info is None: raise Exception # FIXME: Handle error in AJAX request? user = self.get_logged_in_user() if user is None: return None tweet = Tweet.get_or_create(tweet_info) return VoteModel.get_or_create(user, tweet, count)
def search(): """ Handles post request from index page search form, processes form and calls manager functions accordingly. Passes results to rendered search template, or redirects user to homepage if exception occurs. :return: Rendered template with search results """ from app import manager from app import helper import time page = "search" if request.method == 'POST': # Get form data with trailing spaces removed keyword = request.form['keyword'].strip() count = request.form['count'].strip() location = request.form['location'].strip() # Check if search term has been provided if not keyword: flash("Blessed are the forgetful! Looks like you didn't enter a search parameter.") return redirect(url_for('index')) # If user hasn't specified a number of Tweets to search for, use defualt (15) if not count: count = config.TWEET_SEARCH_LIMIT_DEFAULT # If count provided, check to sure it does not exceed max (100) if int(count) > config.TWEET_SEARCH_LIMIT_MAX: # If exceeds max, set count to max count = config.TWEET_SEARCH_LIMIT_MAX # Check if supplied count is negative if int(count) <= 0: flash("Please enter a valid number of Tweets to search (1-100)") return redirect(url_for('index')) user_search = False location_search_term = None # Get time t1 = time.time() # Attempt to perform search, analysis and storage try: if keyword and not location: keyword = keyword.lower() # Check if searching for a user if keyword[0] == "@": user_search = True manager.analysis_supervisor(manager.search_user(keyword, count), keyword) else: manager.analysis_supervisor(manager.search_keyword(keyword, count), keyword) if keyword and location: keyword = keyword.lower() location = location.lower() # Check if searching for a user if keyword[0] == "@": user_search = True manager.analysis_supervisor(manager.search_user(keyword, count), keyword) else: location_search_term = location location = manager.get_geo_info(location_search_term) manager.analysis_supervisor(manager.search_keyword(keyword, count, location), keyword, location_search_term) except Exception as e: # Exception handling for any errors that may occur in retrieving / analyzing / saving data e = str(e) if e == "No Twitter results returned": if location_search_term: flash("Sorry, Twitter returned no results for: \"" + keyword + "\" near " + "\"" + location_search_term + "\"") else: flash("Sorry, Twitter returned no results for: \"" + keyword + "\"") elif e == "Twython auth error": flash("Oops, it appears we're having trouble connecting to Twitter. Please try again later.") elif e == "AlchemyAPI auth error": flash("Oops, it appears we're having trouble connecting to our language processing API. " "Please try again later.") elif e == "AlchemyAPI error": flash("Oops, it appears we had trouble analyzing one or more of the results for that search") elif e == "Not an English language user": flash("Sorry, it appears that account " + keyword + " is not an English language user. Presently, " "Tweetvibe can only analyze English tweets.") elif e == "Location error": flash("Oops, it appears we had trouble identifying location " + "\"" + location_search_term + "\"") elif e == "Database error": flash("Oops, it appears we are experiencing a problem interacting with our database.") else: flash("Oops, something went wrong. A team of highly trained engineer monkeys have been dispatched to" " fix the problem. Please try again later.") # Redirect to index with flash message return redirect(url_for('index')) # Attempt to get results from database try: if location: results = Tweet.objects(Q(keyword_search_term=keyword) & Q(location_address=location.address)).order_by('-stored_at', '-tweet_time').limit(int(count)) else: results = Tweet.objects(keyword_search_term=keyword).order_by('-stored_at', '-tweet_time').limit(int(count)) except: flash("Oops, it appears we are experiencing a problem querying our database.") return redirect(url_for('index')) try: search_aggregate = helper.aggregate_sentiment(results) search_avg = helper.get_query_sentiment_avg(results) except: flash("Oops, something went wrong. A team of highly trained engineer monkeys have been dispatched to" " fix the problem. Please try again later.") return redirect(url_for('index')) # Calculate time taken to perform search and analysis t2 = time.time() time_taken = t2 - t1 if location: # Format location latitude/longitude to 2 decimal places longitude = "{:.2f}".format(float(location.longitude)) latitude = "{:.2f}".format(float(location.latitude)) hist_avg = helper.get_historical_sentiment_avg(keyword, location.address) hist_data = helper.get_historical_sentiment(keyword, location.address) hist_predominant_sentiment = helper.predominant_sentiment(hist_data) return render_template( "search.html", time_taken=time_taken, results=results, page=page, keyword=keyword, location_search_term=location_search_term, location=location.address, longitude=longitude, latitude=latitude, search_count=count, search_aggregate=search_aggregate, search_avg=search_avg, search_stats=helper.get_query_statistics(results, search_aggregate), dom_sentiment=hist_predominant_sentiment, hist_data=hist_data, hist_avg=hist_avg, overtime_data=helper.get_sentiment_overtime(keyword, location.address) ) elif user_search: hist_avg = helper.get_historical_sentiment_avg(keyword) hist_data = helper.get_historical_sentiment(keyword) hist_predominant_sentiment = helper.predominant_sentiment(hist_data) return render_template( "search.html", results=results, time_taken=time_taken, page=page, user=keyword, search_count=count, search_aggregate=search_aggregate, search_avg=search_avg, search_stats=helper.get_query_statistics(results, search_aggregate), dom_sentiment=hist_predominant_sentiment, hist_data=hist_data, hist_avg=hist_avg, overtime_data=helper.get_sentiment_overtime(keyword) ) else: hist_avg = helper.get_historical_sentiment_avg(keyword) hist_data = helper.get_historical_sentiment(keyword) hist_predominant_sentiment = helper.predominant_sentiment(hist_data) return render_template( "search.html", results=results, time_taken=time_taken, page=page, keyword=keyword, search_count=count, search_aggregate=search_aggregate, search_avg=search_avg, search_stats=helper.get_query_statistics(results, search_aggregate), dom_sentiment=hist_predominant_sentiment, hist_data=hist_data, hist_avg=hist_avg, overtime_data=helper.get_sentiment_overtime(keyword) ) else: return redirect(url_for('index'))