-
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 🎉 slack bot to review charts * 🔨 Refactor Slack message handling into dedicated helper functions * 🔨 Refactor Slack message handling into dedicated helper functions * 🔨 Refactor Slack message sending to use helper function * remove streamlit dependency from data module * utils to get slack channels * add createdAt * wip * 🎉 slack bot housekeeper * add ORM for housekeeper * tools to get reviews * wip * fixes * slowly remove streamlit dependency from pure backend code * re-structure command for chart review * prettier * adjust script
- Loading branch information
1 parent
0612d91
commit 5209a2e
Showing
16 changed files
with
282 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,43 +1,5 @@ | ||
import json | ||
from typing import Any, Dict | ||
|
||
from slack_sdk import WebClient | ||
|
||
from etl import config | ||
|
||
slack_client = WebClient(token=config.SLACK_API_TOKEN) | ||
|
||
|
||
def prune_none(d: Dict[str, Any]) -> Dict[str, Any]: | ||
return {k: v for k, v in d.items() if v is not None} | ||
|
||
|
||
def send_slack_message(channel: str, message: str) -> None: | ||
if config.SLACK_API_TOKEN: | ||
slack_client.chat_postMessage(channel=channel, text=message) | ||
|
||
|
||
def format_slack_message(method, url, status_code, req_body, res_body): | ||
try: | ||
res_body = json.dumps(json.loads(res_body), indent=2) | ||
except json.decoder.JSONDecodeError: | ||
pass | ||
|
||
try: | ||
req_body = json.dumps(json.loads(req_body), indent=2) | ||
except json.decoder.JSONDecodeError: | ||
pass | ||
|
||
if status_code == 200: | ||
emoji = ":information_source:" | ||
else: | ||
emoji = ":warning:" | ||
|
||
message = f"{emoji} *{method}* {url}\n" | ||
|
||
if req_body: | ||
message += f"Request\n```\n{req_body}\n```\n" | ||
|
||
message += f"Response\n```\n{res_body}\n```\n" | ||
|
||
return message |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
"""Assist with housekeeping tasks. | ||
The initial motivation for this was to help with the problem of chart maintenance: | ||
"A growing problem we have at OWID is that our database contains a very high number of charts, and this number keeps growing month by month. Many charts are good and worth keeping, but several hundred at least are charts that aren't maintained, updated, and generally up to our current standards. | ||
These charts get few views but still "clog" our internal admin and search results (on OWID and search engines). Overall, these charts take mental space that we could instead allocate to maintaining our most important charts." | ||
TODOs: | ||
Add option of regular reviews of datasets, etc. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
from datetime import datetime | ||
|
||
import pandas as pd | ||
|
||
from apps.housekeeper.utils import add_reviews, get_reviews_id | ||
from apps.wizard.app_pages.similar_charts.data import get_raw_charts | ||
from etl.config import OWID_ENV | ||
from etl.slack_helpers import send_slack_message | ||
|
||
CHANNEL_NAME = "#lucas-playground" | ||
SLACK_USERNAME = "housekeeper" | ||
|
||
|
||
def get_charts_to_review(): | ||
df = get_raw_charts() | ||
|
||
# Keep only older-than-a-year charts | ||
TODAY = datetime.today() | ||
YEAR_AGO = TODAY.replace(year=TODAY.year - 1) | ||
df = df.loc[df["created_at"] < YEAR_AGO] | ||
|
||
# Discard charts already presented in the chat | ||
reviews_id = get_reviews_id(object_type="chart") | ||
df = df.loc[~df["chart_id"].isin(reviews_id)] | ||
|
||
return df | ||
|
||
|
||
def select_chart(df: pd.DataFrame): | ||
# Sort by views | ||
df = df.sort_values(["views_365d", "views_14d", "views_7d"]) | ||
|
||
# Select oldest chart | ||
chart = df.iloc[0] | ||
|
||
return chart | ||
|
||
|
||
def send_slack_chart_review(channel_name: str, slack_username: str, icon_emoji: str): | ||
# Get charts | ||
df = get_charts_to_review() | ||
|
||
# Select chart | ||
chart = select_chart(df) | ||
|
||
# Prepare message | ||
DATE = datetime.today().date().strftime("%d %b, %Y") | ||
|
||
message = ( | ||
f"{DATE}: *Daily chart to review is...*\n" | ||
f"<{OWID_ENV.chart_site(chart['slug'])}|{chart['title']}> ({chart['views_365d']} views in the last year)\n" | ||
f"Go to <{OWID_ENV.chart_admin_site(chart['chart_id'])}|edit :writing_hand:>\n" | ||
) | ||
|
||
# Send message | ||
send_slack_message( | ||
channel=channel_name, | ||
message=message, | ||
icon_emoji=icon_emoji, | ||
username=slack_username, | ||
) | ||
|
||
# Add chart to reviewed charts | ||
add_reviews(object_type="chart", object_id=chart["chart_id"]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
"""Keep things in OWID catalog clean by regularly checking and reviewing content.""" | ||
|
||
import click | ||
from rich_click import RichCommand | ||
|
||
from apps.housekeeper.charts import send_slack_chart_review | ||
|
||
# TODO: Add more review types | ||
REVIEW_TYPES = [ | ||
"chart", | ||
# "dataset", | ||
] | ||
|
||
# Config | ||
CHANNEL_NAME = "#lucas-playground" | ||
SLACK_USERNAME = "housekeeper" | ||
ICON_EMOJI = "sus-blue" | ||
|
||
|
||
@click.command("housekeeper", cls=RichCommand, help=__doc__) | ||
@click.option("--review-type", "-t", type=click.Choice(REVIEW_TYPES, case_sensitive=False)) | ||
def main(review_type: str): | ||
# Review charts | ||
if review_type == "chart": | ||
send_slack_chart_review( | ||
channel_name=CHANNEL_NAME, | ||
slack_username=SLACK_USERNAME, | ||
icon_emoji=ICON_EMOJI, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from sqlalchemy.orm import Session | ||
|
||
from etl.config import OWID_ENV | ||
from etl.grapher import model as gm | ||
|
||
|
||
def get_reviews_id(object_type: str): | ||
with Session(OWID_ENV.engine) as session: | ||
return gm.HousekeepingSuggestedReview.load_reviews_object_id(session, object_type=object_type) | ||
|
||
|
||
def add_reviews(object_type: str, object_id: int): | ||
with Session(OWID_ENV.engine) as session: | ||
gm.HousekeepingSuggestedReview.add_review( | ||
session=session, | ||
object_type=object_type, | ||
object_id=object_id, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.