<filename>sentiment.py
import boto3
import click
import subprocess
import typing

try:
    from enchant.checker import SpellChecker
    from enchant.tokenize import EmailFilter, URLFilter
except Exception:
    pass


@click.command()
@click.argument('text', nargs=-1)
def cli(text):
    combined_text = ' '.join(text)
    if len(combined_text) == 0:
        try:
            pbpaste_result = subprocess.check_output(['pbpaste'])
            combined_text = str(pbpaste_result, 'utf8')
        except (FileNotFoundError, subprocess.SubprocessError):
            combined_text = 'hi, how are you? it is lovely to meet'

    aws_session = boto3.Session()
    comprehend_client = aws_session.client('comprehend')

    resp = comprehend_client.detect_sentiment(
        LanguageCode='en',
        Text=combined_text
    )
    sentiment = resp['Sentiment']
    scores = resp['SentimentScore']
    positive = scores['Positive']
    negative = scores['Negative']
    neutral = scores['Neutral']
    mixed = scores['Mixed']

    if sentiment == 'POSITIVE':
        color = 'green'
    elif sentiment == 'NEGATIVE':
        color = 'red'
    elif sentiment == 'MIXED':
        color = 'yellow'
    else:
        color = 'white'
    click.echo(combined_text)
    click.echo('Sentiment: ', nl='')
    sentiment_styled = click.style(sentiment, fg=color, bold=True)
    click.echo(sentiment_styled)

    chart_char = '▇'
    chart_width = 50
    click.echo('positive: ' + click.style(chart_char * round(positive * chart_width), fg='green') + f' {positive:.2f}')
    click.echo('negative: ' + click.style(chart_char * round(negative * chart_width), fg='red') + f' {negative:.2f}')
    click.echo('mixed   : ' + click.style(chart_char * round(mixed * chart_width), fg='yellow') + f' {mixed:.2f}')
    click.echo('neutral : ' + click.style(chart_char * round(neutral * chart_width), fg='white') + f' {neutral:.2f}')

    word_count = len(combined_text.split())
    _reminders(word_count, sentiment_styled, combined_text)


def _reminders(word_count: int, sentiment_styled: str, combined_text: str):
    click.echo()
    click.echo('\tTips for communicating over slack and email:')

    word_count_color = 'green'
    if word_count < 10:
        word_count_color = 'red'
    elif word_count < 30:
        word_count_color = 'yellow'

    typos = _typos(combined_text)
    typo_text = ''
    if typos is not None and not typos:
        typo_text = click.style(' No 🎉', fg='green')
    elif typos:
        typo_text = click.style(f' Yes: {len(typos)}', fg='red', bold=True)

    click.echo(click.style('\t\t1. Is my email/slack too brief?', bold=True) + click.style(f' Words: {word_count}', fg=word_count_color))
    click.echo(click.style('\t\t2. Are there any typos?', bold=True) + typo_text)
    click.echo(click.style('\t\t3. What tone am I projecting?', bold=True) + f' Sentiment is {sentiment_styled}')
    click.echo(click.style('\t\t4. Would it be helpful to talk instead?', bold=True))
    click.echo(f'\t\t\tProbably! Make sure to consider this especially if this thread has gone back and forth more than twice.')
    click.echo()

    if typos:
        click.echo(click.style('Typos:', bold=True))
        for typo in typos:
            click.echo(typo)
        click.echo()


def _typos(text: str) -> typing.Optional[typing.List[str]]:
    lines = text.splitlines()
    total_lines_width = len(str(len(lines)))
    typos = []
    try:
        for i, line in enumerate(lines):
            y = (total_lines_width - len(str(i+1))) * ' ' + str(i+1)
            this_line_width = len(str(len(line)))
            chckr = SpellChecker('en_US', filters=[EmailFilter, URLFilter])
            chckr.set_text(text)
            for err in chckr:
                x = (this_line_width - len(str(err.wordpos))) * ' ' + str(err.wordpos)
                typos.append(f'spelling error [{y}, {x}]: {err.word}')
    except Exception:
        return None
    return typos


if __name__ == '__main__':
    cli()
