Skip to content
This repository has been archived by the owner on Oct 21, 2024. It is now read-only.

Commit

Permalink
Moving CLI functions to a discoverable location (#257)
Browse files Browse the repository at this point in the history
  • Loading branch information
alysivji authored Apr 19, 2020
1 parent 9f104db commit 2ae9af8
Show file tree
Hide file tree
Showing 35 changed files with 232 additions and 221 deletions.
38 changes: 32 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ Busy Beaver welcomes any, and all, contributions. Every little bit helps!
- [Setting up Development Environment](#setting-up-development-environment)
- [Verify Installation](#verify-installation)
- [Running Tests](#running-tests)
- [Adding New Requirements](#adding-new-requirements)
- [Modifying Third-Party Integrations](#modifying-third-party-integrations)
- [Adding New Third-Party Integrations](#adding-new-third-party-integrations)
- [Developer Documentation](#developer-documentation)
- [Adding New Requirements](#adding-new-requirements)
- [Modifying Third-Party Integrations](#modifying-third-party-integrations)
- [Adding New Third-Party Integrations](#adding-new-third-party-integrations)
- [Creating new CLI Command](#creating-new-cli-command)
- [Slack Slash Commands](#slack-slash-commands)
- [Task Queues](#task-queues)
- [Creating a New Task](#creating-a-new-task)
Expand Down Expand Up @@ -124,7 +126,9 @@ run pytest with:
$ make test
```

## Adding New Requirements
## Developer Documentation

### Adding New Requirements

Busy Beaver uses [`pip-tools`](https://github.com/jazzband/pip-tools) to
manage `pip` dependencies.
Expand All @@ -137,14 +141,36 @@ run the following command:
You will need to have `pip-tools` installed on your local machine to
perform this actin.

## Modifying Third-Party Integrations
### Modifying Third-Party Integrations

As each integration requires API credentials, it is recommended that contributors create apps for integration connect to their personal accounts.

## Adding New Third-Party Integrations
### Adding New Third-Party Integrations

Provide detailed instructions on how to set up the integration so we can roll the feature out to the production instance of Busy Beaver with correct credentials.

### Creating new CLI Command

Create a `cli.py` module. Place this in your application folder next to `blueprint.py`.

```python
# cli.py

import logging

import click

from .blueprint import bp

logger = logging.getLogger(__name__)


@click.option("--workspace", required=True)
@bp.cli.command("function_name", help="Help prompt")
def function_name(*args, **kwargs)):
pass
```

## Slack Slash Commands

Users are able to interact with Busy Beaver using the `/busybeaver [command]` interface provided through the Slack UI. All slash commands are routed to a Busy Beaver endpoint that was enabled earlier via the Slack Slash Command webhook.
Expand Down
24 changes: 0 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,27 +83,3 @@ We are grateful to the following organizations for providing free services to op
- [GitHub](https://github.com) (code repo, issues)
- [DockerHub](https://hub.docker.com) (hosting Docker images)
- [Travis CI](https://travis-ci.org/) (continuous integration platform)

### API Docs

Can make requests to REST endpoints to kick off processes.

#### GitHub Summary Endpoint

- Start the process to run a summary by making a `POST` request to `/poll/twitter` with `Authentication` header set to `token {token}` and JSON body:

```json
{
"channel": "busy-beaver"
}
```

#### Retweeter Endpoint

- Check Twitter feed for new posts to share on Slack by making a `POST` request to `/poll/github-summary` with `Authentication` header set to `token {token}` and JSON body:

```json
{
"channel": "at-chicagopython"
}
```
2 changes: 1 addition & 1 deletion busy_beaver/apps/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ Flask code is in `api` subfolders.
|Application|Description
|---|---|
|debug|Tools to help development and debug|
|events|Find and post upcoming events|
|github_integration|GitHub related-integration logic|
|retweeter|Shares tweets by a given Twitter account in Slack|
|slack_integration|Slack-related integration logic|
|upcoming_events|Display upcoming events from Meetup
|youtube_integration|YouTube related-itnegration logic (WIP)|
5 changes: 5 additions & 0 deletions busy_beaver/apps/events/blueprint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from flask import blueprints

events_bp = blueprints.Blueprint("events", __name__)

from . import cli # noqa isort:skip
33 changes: 33 additions & 0 deletions busy_beaver/apps/events/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import logging

import click

from .blueprint import events_bp
from .sync_database import sync_database_with_fetched_events
from .upcoming_events import generate_upcoming_events_message
from busy_beaver.clients import SlackClient
from busy_beaver.models import SlackInstallation

logger = logging.getLogger(__name__)


@click.option("--count", default=5, required=True, help="Number of events to post")
@click.option("--group_name", required=True, help="Meetup group name")
@click.option("--channel", required=True, help="Slack channel")
@click.option("--workspace", required=True, help="Slack workspace ID")
@events_bp.cli.command(
"post_upcoming_events", help="Post Upcoming Events Summary to Slack channel"
)
def post_upcoming_events_message_to_slack_cli(
workspace: str, channel: str, group_name: str, count: int
):
blocks = generate_upcoming_events_message(group_name, count)
installation = SlackInstallation.query.filter_by(workspace_id=workspace).first()
slack = SlackClient(installation.bot_access_token)
slack.post_message(blocks=blocks, channel=channel)


@click.option("--group_name", required=True, help="Meetup group name")
@events_bp.cli.command("sync_events_database", help="Sync Events Database")
def sync_events_database_cli(group_name: str):
sync_database_with_fetched_events(group_name)
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
"""Update Events Database Workflow
This workflow pulls a set of events from Meetup and performs the following action
against the set of future events in the database:
- if event is new => create
- if event is in the database, but not in the fetched events => delete
- if event is in the database => update
"""

import logging
import time
from typing import List, NamedTuple

from busy_beaver.clients import meetup
from busy_beaver.common.wrappers.meetup import EventDetails
from busy_beaver.extensions import db
from busy_beaver.models import Event

logger = logging.getLogger(__name__)


def sync_database_with_fetched_events(group_name):
fetched_events = meetup.get_events(group_name, count=20)

current_epoch_time = int(time.time())
database_events = Event.query.filter(Event.start_epoch > current_epoch_time).all()

sync_database = SyncEventDatabase(fetched_events, database_events)
sync_database.perform()


class PendingTransactions(NamedTuple):
create: list
update: list
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import time

import click

from .blueprint import events_bp
from busy_beaver.apps.upcoming_events.cards import UpcomingEventList
from busy_beaver.clients import SlackClient
from busy_beaver.apps.events.cards import UpcomingEventList
from busy_beaver.common.wrappers.meetup import EventDetails
from busy_beaver.models import Event, SlackInstallation
from busy_beaver.models import Event


def generate_upcoming_events_message(group_name: str, count: int):
Expand All @@ -21,20 +17,6 @@ def generate_next_event_message(group_name: str):
return _next_event_attachment(event)


@click.option("--count", default=5, required=True)
@click.option("--group_name", required=True)
@click.option("--channel", required=True)
@click.option("--workspace", required=True)
@events_bp.cli.command("post_upcoming_events", help="Post Upcoming Events Summary")
def post_upcoming_events_message_to_slack_cli(
workspace: str, channel: str, group_name: str, count: int
):
blocks = generate_upcoming_events_message(group_name, count)
installation = SlackInstallation.query.filter_by(workspace_id=workspace).first()
slack = SlackClient(installation.bot_access_token)
slack.post_message(blocks=blocks, channel=channel)


def _fetch_future_events_from_database(group_name, count):
# TODO: for multi-tenant we will need use group_name in query
current_epoch_time = int(time.time())
Expand Down
2 changes: 1 addition & 1 deletion busy_beaver/apps/github_integration/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
github_bp = blueprints.Blueprint("github", __name__)

from . import api # noqa isort:skip
from . import summary # noqa isort:skip
from . import cli # noqa isort:skip
26 changes: 26 additions & 0 deletions busy_beaver/apps/github_integration/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from datetime import timedelta
import logging

import click

from .blueprint import github_bp
from .summary.workflow import fetch_github_summary_post_to_slack
from busy_beaver.exceptions import ValidationError
from busy_beaver.models import SlackInstallation
from busy_beaver.toolbox import utc_now_minus

logger = logging.getLogger(__name__)


@click.option("--workspace", required=True, prompt="Slack workspace ID")
@github_bp.cli.command("post_github_summary", help="Post a GitHub summary")
def post_github_summary_to_slack_cli(workspace: str):
boundary_dt = utc_now_minus(timedelta(days=1))
installation = SlackInstallation.query.filter_by(workspace_id=workspace).first()
if not installation:
raise ValidationError("workspace not found")

# we should log that we did something somewhere
# also keep track of how long a summary took
# TODO once we are migrated over
fetch_github_summary_post_to_slack(installation, boundary_dt)
1 change: 0 additions & 1 deletion busy_beaver/apps/github_integration/summary/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
from . import task # noqa
58 changes: 0 additions & 58 deletions busy_beaver/apps/github_integration/summary/task.py

This file was deleted.

36 changes: 36 additions & 0 deletions busy_beaver/apps/github_integration/summary/workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import logging
import random
from typing import List

from sqlalchemy import and_

from .blocks import GitHubSummaryPost
from busy_beaver.common.wrappers import SlackClient
from busy_beaver.models import GitHubSummaryUser

logger = logging.getLogger(__name__)


def fetch_github_summary_post_to_slack(installation, boundary_dt):
channel = installation.github_summary_config.channel
slack = SlackClient(installation.bot_access_token)

channel_members = slack.get_channel_members(channel)
users: List[GitHubSummaryUser] = GitHubSummaryUser.query.filter(
and_(
GitHubSummaryUser.config_id == installation.github_summary_config.id,
GitHubSummaryUser.slack_id.in_(channel_members),
GitHubSummaryUser.github_username.isnot(None),
)
).all()
random.shuffle(users)

github_summary_post = GitHubSummaryPost(users, boundary_dt)
github_summary_post.create()

slack.post_message(
blocks=github_summary_post.as_blocks(),
channel=channel,
unfurl_links=False,
unfurl_media=False,
)
2 changes: 1 addition & 1 deletion busy_beaver/apps/retweeter/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

twitter_bp = blueprints.Blueprint("twitter", __name__)

from . import task # noqa isort:skip
from . import cli # noqa isort:skip
19 changes: 19 additions & 0 deletions busy_beaver/apps/retweeter/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import logging

import click

from .blueprint import twitter_bp
from .workflow import fetch_tweets_post_to_slack
from busy_beaver.config import TWITTER_USERNAME
from busy_beaver.models import SlackInstallation

logger = logging.getLogger(__name__)


@click.option("--channel_name", required=True, help="Slack channel")
@click.option("--workspace", required=True, help="Slack workspace ID")
@twitter_bp.cli.command("poll_twitter", help="Find new tweets to post to Slack")
def poll_twitter(channel_name: str, workspace: str):
# TODO add logging and times
installation = SlackInstallation.query.filter_by(workspace_id=workspace).first()
fetch_tweets_post_to_slack(installation, channel_name, username=TWITTER_USERNAME)
Loading

0 comments on commit 2ae9af8

Please sign in to comment.