Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Simplify the code #89

Merged
merged 5 commits into from
Oct 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
2 changes: 1 addition & 1 deletion twitter-sentiment/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
./h2o_wave-nightly-py3-none-any.whl
tweepy==3.9.0
h2o-wave<1.0
vaderSentiment
125 changes: 33 additions & 92 deletions twitter-sentiment/src/app.py
Original file line number Diff line number Diff line change
@@ -1,103 +1,44 @@
import os
from typing import Tuple
from h2o_wave import Q, app, ui, main, data
from h2o_wave import Q, app, ui, main

from .tweet_analyser import TweetAnalyser

def configure_page(q: Q, bypass_login):
q.page['twitter_app'].dialog = None
q.page['search'] = ui.form_card(box='search', items=[ui.textbox(name='search', trigger=True, icon='Search')])
q.client.tweet_analyser = TweetAnalyser(q.app.env_vars[0], q.app.env_vars[1], q.app.env_vars[2], q.app.env_vars[3]) if bypass_login else TweetAnalyser(q.args.access_token, q.args.access_token_secret, q.args.consumer_key, q.args.consumer_secret)


def init(q: Q):
q.app.env_vars.append(os.environ.get("ACCESS_TOKEN"))
q.app.env_vars.append(os.environ.get("ACCESS_TOKEN_SECRET"))
q.app.env_vars.append(os.environ.get("CONSUMER_KEY"))
q.app.env_vars.append(os.environ.get("CONSUMER_SECRET"))
q.page['header'] = ui.header_card(
box='header',
title='Twitter Sentiment',
subtitle='Searches twitter hashtags and sentiment analysis',
icon='UpgradeAnalysis',
icon_color='#00A8E0',
)
if not (None in q.app.env_vars):
# Existing login credentials in environment variables --> bypass login
configure_page(q, True)
search_tweets(q)
q.page['twitter_app'] = ui.meta_card(
box='',
title='Twitter Sentiment',
layouts=[ui.layout('xs', zones=[
ui.zone('header'),
ui.zone('search'),
ui.zone('twitter_cards', direction=ui.ZoneDirection.ROW, wrap='stretch', justify='center')
])]
)
else:
# Missing login credentials in environment variables --> required login
q.page['twitter_app'] = ui.meta_card(
box='',
@app('/')
async def serve(q: Q):
if not q.app.initialized:
q.app.positive, q.app.neutral, q.app.negative = await q.site.upload(
['static/positive.svg', 'static/neutral.svg', 'static/negative.svg'])
q.app.initialized = True
if not q.client.initialized:
q.page['header'] = ui.header_card(
box='header',
title='Twitter Sentiment',
dialog= ui.dialog(title='Twitter Credentials', primary=True, items=[
ui.markup('Apply for access : <a href="https://developer.twitter.com/en/apply-for-access" target="_blank">Visit developer.twitter.com!</a>'),
ui.textbox(name='consumer_key', label='Consumer Key', required=True, password=True),
ui.textbox(name='consumer_secret', label='Consumer Secret', required=True, password=True),
ui.textbox(name='access_token', label='Access Token', required=True, password=True),
ui.textbox(name='access_token_secret', label='Access Token Secret', required=True, password=True),
ui.buttons(items=[ui.button(name='configure', label='Configure', primary=True)], justify='end')
]),
layouts=[ui.layout('xs', zones=[
ui.zone('header'),
ui.zone('search'),
ui.zone('twitter_cards', direction=ui.ZoneDirection.ROW, wrap='stretch', justify='center')
])]
subtitle='Searches twitter hashtags and sentiment analysis',
image='https://www.h2o.ai/wp-content/themes/h2o2018/templates/dist/images/h2o_logo.svg',
items=[ui.textbox(name='search', trigger=True, icon='Search', width='400px', value='AI')]
)
q.page['twitter_app'] = ui.meta_card(box='', title='Twitter Sentiment', layouts=[ui.layout('xs', zones=[
ui.zone('header'),
ui.zone('twitter_cards', direction=ui.ZoneDirection.ROW, wrap='stretch', justify='center')
])])
q.client.tweet_analyser = TweetAnalyser()
q.client.initialized = True

def get_sentiment(polarity) -> Tuple[str, str]:
compound = polarity['compound']
if compound > 0:
return 'success', 'Positive'
elif compound == 0:
return 'warning', 'Neutral'
else:
return 'error', 'Negative'


def search_tweets(q: Q):
# TODO: Remove after https://github.com/h2oai/wave/issues/150 resolved.
q.page['search'].items[0].textbox.value = q.args.search or 'AI'
for i, tweet in enumerate(q.client.tweet_analyser.search_tweets(q=q.args.search or 'AI')):
if not tweet.retweeted:
polarity = q.client.tweet_analyser.get_polarity_scores(tweet.text)
message_type, sentiment = get_sentiment(polarity)
plot_rows = [
(polarity['neg'], 'Negative'),
(polarity['neu'], 'Neutral'),
(polarity['pos'], 'Positive'),
]
q.page[f'twitter_card_{i}'] = ui.form_card(box=ui.box('twitter_cards', width='400px'), items=[
ui.message_bar(type=message_type, text=f'Sentiment - {sentiment}'),
ui.visualization(
plot=ui.plot([ui.mark(type='interval', x='=sentiment', y='=value', color='=sentiment', color_range='$red $yellow $green')]),
data=data(['value', 'sentiment'], rows=plot_rows, pack=True),
),
ui.text(f'_{tweet.text}_'),
])


@app('/')
async def serve(q: Q):
q.app.env_vars = []
if not q.client.initialized:
init(q)
q.client.initialized = True
if q.args.configure:
# Grant access after login credentials are manually passed
configure_page(q, False)
search_tweets(q)
elif q.args.search:
search_tweets(q)
compound = q.client.tweet_analyser.get_polarity_scores(tweet.text)['compound']
if compound > 0:
sentiment = 'positive'
elif compound == 0:
sentiment = 'neutral'
else:
sentiment = 'negative'
q.page[f'twitter_card_{i}'] = ui.profile_card(
box=ui.box('twitter_cards', width='400px'),
persona=ui.persona(title=tweet.user.name, image=tweet.user.profile_image_url),
image=q.app[sentiment],
items=[ui.text(f'_{tweet.text}_')],
height='170px'
)

await q.page.save()
11 changes: 8 additions & 3 deletions twitter-sentiment/src/tweet_analyser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
from tweepy.auth import OAuthHandler
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer

ACCESS_TOKEN = '2944447934-6KB33SzWsywpjYuibVLlKDFldCbYq0OcP7jJRg2'
ACCESS_TOKEN_SECRET = 'teBNYDJovG8lNrwUk8w2TWErR7gEaGRHTdI105dcw0yV5'
CONSUMER_KEY = 'TwgJUSNeivHHOJXLj9dUQzMOp'
CONSUMER_SECRET = 'cXHG8F298S5SYr0wVobZqZLH7qmOGyPghYGbH9Q6ZVb2KuchIr'


class TweetAnalyser:
"""
Expand All @@ -13,10 +18,10 @@ class TweetAnalyser:
with a minimal change to the app code.
"""

def __init__(self, access_token, access_token_secret, consumer_key, consumer_secret):
def __init__(self):
self.analyser = SentimentIntensityAnalyzer()
self.auth_handler = OAuthHandler(consumer_key, consumer_secret)
self.auth_handler.set_access_token(access_token, access_token_secret)
self.auth_handler = OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
self.auth_handler.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
self.api = API(self.auth_handler, wait_on_rate_limit=True, wait_on_rate_limit_notify=True)

def search_tweets(self, q: str, lang='en', rpp=100, items=12):
Expand Down
4 changes: 4 additions & 0 deletions twitter-sentiment/static/negative.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions twitter-sentiment/static/neutral.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions twitter-sentiment/static/positive.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.